Black Hole organizacion
CostaRica  
  Bienvenida
  Hackers
  Foro ingresa
  Noticias
  Contacto
  Imagenes
  Programas
  Visitantes
  Chat
  Libros
  => hacker
  => Virus
  => Cracker
  => Lecciones hackin
  => Programacion
  => Diseños web
  => Guia de hackin
  => Privasidad
  => Guia → 1
  => Guia → 2
  => Guia → 3
  => Guia → 4
  => Guia → 5
  => Guia → 6
  => Guia → 7
  => Guia → 8
  => Guia → 9
  => Guia → 10
  => Guia → 11
  => Como intrudusirse aun sistema
  => shellcodes_linux-1
  => shellcodes_linux -2
  => UN-scodes.
  => Guia version Deluxe
  => Ser Hacker dentro de Términos Legales
  => Como hackear una paginaweb
  => Comoprogramar un Virus
  => Como Crear un Virus
  => Cuantos tipos de Virus existen
  => Programaciones de un virus
  => Estructura de computadores
  => Fundamentos de SSOO
  => Sistemas de numeración
  => Ensamblador I: Conceptos básicos
  => Ensamblador II
  => Utilidades para la programación
  => Infección bajo Windows
  => Infección bajo Linux
  => Técnicas avanzadas
  => Apéndices
  => CONOCIENDO LA MAQUINA
  => DIRECCIONAMIENTO DE MEMORIA EN EL 8086
  => CHIPS DE APOYO (Ampliación de la lección 1)
  => LA PILA DEL 8086
  => CODIFICACIÓN DE LAS INSTRUCCIONES EN EL 8086
  => Manual HTML
  => Ataques basados en Desbordamiento de Buffer (Buffer Overflow)
  => Privasidad
  => Escaneo
  => anti Escaneo y Escaneo
  => Malianom
  Triang
  Tersirve esta paguina?
  -
  juegos
  Vagos
  mapa
  Mapa del sitio
  546
Fundamentos de SSOO

Atras


 

2.1.- Introducción y funciones de los SSOO

La misión del sistema operativo es dar una serie de programas al ordenador que permitan una utilización cómoda del computador, dotándolo de toda una serie de funciones:

- Gestión de los recursos del computador: Debe de controlar a este nivel la asignación de recursos a los programas libres en ejecución, recuperación de recursos cuando los programas no los necesitan. Será lo que conoceremos como "nivel kernel".

- Ejecución de servicios para los programas: Estos servicios incluirán varios para lanzar la ejecución de un programa, comunicar unos con otros, operar con la E/S, sobre ficheros, y el tratamiento y solución de errores. Lo llamaremos más adelante "nivel API"

- Ejecución de los mandatos de los usuarios: Es el módulo del sistema operativo que permite que los usuarios dialoguen de forma interactiva con el sistema, conocido como "nivel shell".

Analizaremos estos tres niveles en detalle.

 

 

Las tres capas, kernel, API y Shell, siendo kernel la más cercana al computador y shell la más cercana al usuario

 

 

2.2.- Nivel Kernel

Decíamos en el anterior punto que el "nivel kernel" es el que se encarga de la gestión de los recursos del computador; por así decirlo, el kernel es la parte más interna de un sistema operativo, la que maneja las cosas más básicas que este posee y da la base para que podamos utilizarlo. Realizará la gestión básica de procesos (un proceso es a grandes rasgos un programa ejecutándose, con su propio espacio virtual de direcciones de memoria tal y como indicábamos en la parte dedicada a la memoria en el capítulo primero), así como va a ser el encargado de proteger unos programas de ser accedidos por otros, va a realizar el mantenimiento del sistema de ficheros, etc. Podemos definir sus tareas como:

- Asignación de recursos: Proporcionarlos para aquellos programas que se encuentran en ejecución, manteniendo para ello estructuras que le permitan saber qué recursos están libres y cuáles están asignados a cada programa, teniendo en cuenta la disponibilidad de los mismos; es importante la recuperación de estos recursos cuando los programas ya no los necesitan. Una recuperación mal hecha puede hacer por ejemplo que el sistema operativo considere que no tiene memoria, cuando en realidad si la tiene.

- Protección: Ha de garantizarse en este nivel que existe protección entre los usuarios del sistema, y que la información ha de ser confidencial, asegurándose de que unos trabajos no interfieran con otros, impidiendo que unos programas puedan acceder a los recursos de otros.

 

 

2.3.- Nivel API

Consiste en una serie de servicios que los programas pueden solicitar, complementando los que el hardware proporciona. Si sólo contásemos con lo que nos da el hardware como servicios, al programar tendríamos por ejemplo que abrir ficheros localizándolos físicamente en el disco duro; con esta API, se nos pueden proporcionar funciones software que nos liberen de esta tarea y nos faciliten las cosas convirtiendo una lectura de un sector del disco duro en algo tan sencillo como "abrir fichero X" y "leer fichero X", abstrayendo el cúmulo de datos existente en el HD en estas estructuras llamadas ficheros en lugar de acceder a ellos diréctamente. Así, tenemos estas cuatro clases de servicios:

- Ejecución de programas: Se proporcionan funciones para lanzar la ejecución de un programa así como para pararla o abortarla, junto con otros que sirvan para conocer y modificar las condiciones de ejecución de los programas, para comunicar y sincronizar unos programas con otros. Como ejemplos tenemos funciones en sistemas operativos Unix como exec (ejecutar un programa) o fork (reproducir el proceso actual en otro espacio virtual), o en otros como Windows, algunos como CreateProcess (crear proceso, indicando qué ejecutar para él) .

- Operaciones de E/S: Proveen de operaciones de lectura, escritura y modificación del estado de los periféricos; la programación de estas operaciones de E/S es compleja y depende del hardware en particular utilizado, ofreciéndose con estos servicios un nivel alto de abstracción para que el programador de aplicaciones no haya de preocuparse de estos detalles. Los servicios dados por una tarjeta gráfica gracias a su driver, por ejemplo, tendrían como objetivo ocultar la complejidad del hardware específico de esta tarjeta haciendo que el programador sólo tenga que escribir sus rutinas una vez, y según la implementación particular del driver con los servicios comunes, éste solucione la forma de transformar lo programado en algo físico (la imagen en pantalla).

- Operaciones sobre ficheros: Ofrecen un nivel mayor en abstracción que las operaciones de E/S, orientadas en este caso a ficheros y por lo tanto a operaciones como la creación, borrado, renombrado, apertura, escritura y lectura de ficheros, llevadas a cabo con funciones como en Windows serían CreateFile, OpenFile o CloseFile, o en sistemas Posix (Unix, Linux...) funciones como open(), readdir(), close(), etc.

- Detección y tratamiento de errores: Se trata de la parte en que se controlan los posibles errores que puedan detectarse.

 

 

2.4.- Nivel de Shell

Se trata de la parte del sistema que se encarga de atender y llevar a cabo las peticiones de los usuarios del computador, proporcionando una serie de funciones básicas que el usuario pueda llevar a cabo. El nivel de abstracción es mayor que la API, y permite que por ejemplo al borrar un fichero el usuario tenga simplemente que ejecutar "del fichero" en un sistema Dos, o "rm fichero" en un Posix (Unix, Linux..) en lugar de tener que programar un ejecutable que borre ese fichero llamando a funciones de la API.

El shell es el interfaz con el que interactuamos normalmente, que intenta crear un entorno acogedor para el usuario, intuitivo; uno de los objetivos que se necesitan obtener, es que se facilite el trabajo a los usuarios novatos pero que al tiempo esto no destruya la productividad de los usuarios más avanzados. Tenemos entonces shells de tipo alfanumérico (modo terminal en Unix, o la clásica ventana Ms-Dos en Windows) donde el modo de trabajo se basa en líneas de texto dadas como instrucciones al sistema, y de tipo gráfico (X-Windows en Unix, entorno gráfico de Windows)

Sea cual sea la forma de presentación, alfanumérica o gráfica, el shell debería de cumplir estas funciones:

- Manipulación de archivos y directorios: La interfaz ha de proporcionar operaciones para crear, borrar, renombrer y procesar archivos y directorios.

- Ejecución de programas: El shell o interfaz ha de proporcionar mecanismos para que desde él se puedan ejecutar programas.

- Herramientas para el desarrollo de aplicaciones: Debe poseer utilidades como compiladores para que el usuario pueda construir sus propias aplicaciones.

- Comunicación con otros sistemas: Han de existir herramientas básicas para acceder a recursos localizados en otros sistemas, como puedan ser telnet o ftp.

- Información de estado del sistema: Utilidades que permitan consultar y/o cambiar cosas como la fecha, hora, número de usuarios conectados al sistema, cantidad de disco y de memoria disponible.

- Configuración: El interfaz ha de ser configurable en su modo de operación según las preferencias del usuario (por ejemplo, formato de fechas y lenguaje).

- Seguridad: En sistemas multiusuario (Unix, Windows NT), la interfaz ha de controlar el acceso de usuarios al sistema para mantener su seguridad.

 

 

2.5.- Arranque del computador

Cuando encendemos el ordenador, podríamos decir que está "desnudo"; el sistema operativo no se ha cargado aún y se encuentra inutilizable para el usuario. Así pues, se realizarán una serie de operaciones antes de darnos el control sobre él.

Primero, se ejecutará el iniciador ROM; en esta memoria se encuentra un programa de arranque siempre disponible (la ROM no puede sobreescribirse) que va a realizar una comprobación del sistema averiguando cosas como la cantidad de memoria disponible y los periféricos instalados, se asegurará de que funcionan mediante un test, y cargará en memoria el programa de carga del sistema operativo, dando después el control a este programa (para dar independencia, suele hacerse que el iniciador ROM sea independiente del sistema operativo instalado).

Si hablamos de arranque desde un disco duro, la ROM cargará en memoria lo que conocemos como MBR o Master Boot Record. Esto, es un sector del disco duro (el primer sector físicamente) de 512 bytes que contiene la tabla de particiones del disco duro. En esta tabla se indicará como se encuentra dividido el HD (podríamos tener por ejemplo un disco duro dividido en una partición de 4 Gb y otra de 2Gb, por cualquiera que fuese el motivo). Además, se va a indicar qué partición es la que contiene el sistema operativo activo que se desea arrancar. Así pues, el programa que hay dentro de la MBR lo que va a hacer es según el contenido de la tabla de particiones (dentro de esta MBR) cargar el "sector boot", programa de carga del sistema operativo, correspondiente al que se desea arrancar. A veces sin embargo queremos tener más de un sistema operativo instalado, como pueda ser la típica combinación Windows/Linux. En esta ocasión, se utilizan programas más complejos, "gestores de arranque", con los que el usuario puede decidir si quiere arrancar uno u otro.

El programa de la MBR (sólo en discos duros, puesto que en un diskette no existe), pasará el control al sector boot o programa de carga del sistema operativo (que ocupa un sector del disco duro, 512 bytes, aunque servirá sencillamente para arrancar el resto). Una vez cargados los componentes se pasa a la fase de inicialización del sistema operativo, lo cual incluye:

- Test del sistema: Completa el test realizado por el iniciador ROM, comprobando también que el sistema de ficheros se encuentra en un estado coherente revisando los directorios.

- Establecimiento de estructuras de información propias del SO, como los sistemas de gestión de procesos, memoria y E/S.

- Carga en memoria principal de las partes del SO que hayan de encontrarse siempre en memoria (SO residente).

- Creación de un proceso de inicio o login por cada terminal definido en el sistema así como una serie de procesos auxiliares y programas residentes en memoria; el proceso de login comprobará que el usuario puede acceder al sistema pidiéndole que se identifique mediante su nombre y clave, aunque esto puede no existir en sistemas Windows.

 

 

 

2.6.- Servidor de ficheros

 

2.6.1.- Estructura de ficheros

Los datos en un disco duro serían un caos sin una estructura capaz de organizarlos y presentarlos de forma coherente. ¿Cómo podemos saber que el sector físico 1537 del disco duro pertenece al fichero que buscamos y en qué zonas se encuentra este repartido?. Para ello, en toda partición existe una estructura (llamada FAT o File Allocation Table, Tabla de Localización de Ficheros, en sistemas Ms-Dos y Windows, o la estructura de i-nodes en sistemas UNIX) dedicada a esta labor. En ella, se va a mantener un registro de los ficheros que pertenecen a la partición y dónde se encuentran físicamente.

Así, mientras nosotros hacemos referencia a un concepto abstracto como sería "el fichero c:documentosdatos.txt", cuando accedamos a él nuestro sistema operativo traducirá esto junto con la FAT a "los datos que se almacenan en los sectores (x1, x2,... xn) del disco duro y que juntos forman una unidad de tamaño Z. En la tabla se indicarán además, otros datos, como puedan ser los atributos del fichero (sólo lectura, oculto, o los permisos de usuario en UNIX), fechas de creación y última modificación, tamaño, etc.

Sin embargo, sabemos que en el disco duro no encontramos todos los ficheros juntos sino que hay una división jerárquica en directorios (me niego a utilizar la palabra "carpetas") y que los ficheros están relacionados con esos directorios perteneciendo a algunos en particular. En realidad y aunque la implementación varíe según el sistema operativo, esto es una falsa sensación; un directorio puede ser un fichero que contenga una serie de nombres, que serán referencia a otros archivos o a otros directorios. Así, habría un directorio raiz (el c:, d:, etc, o un / ) que contendría toda una serie de ficheros, de los cuales algunos serán directorios. Estos directorios a su vez, serán ficheros que funcionan como almacenes de nombres, de los ficheros que contienen y los directorios que cuelgan de él.

 

 

2.6.2.- Acceso a ficheros

Cuando deseamos acceder a los contenidos de los ficheros tenemos dos maneras:

- Uso de punteros: Al abrir un archivo para lectura/escritura, el puntero de acceso se dirigirá al inicio del fichero. Así, si leemos o escribimos en él, la operación se realizará sobre su primer byte. Si modificamos el puntero para que apunte a la dirección 800 del archivo, escribiremos o leeremos sobre ella; este, es el método clásico de leer y modificar datos contenidos en un soporte físico.

- Mapeado en memoria: Bajo varias denominaciones (mapeado en memoria, Memory File Mapping, proyección de ficheros en memoria) se esconde un método muchísimo más cómodo que el de punteros y que en Windows 95 supone uno de los grandes avances de cara a la programación (aunque avance entre comillas dado que el sistema lleva años funcionando bajo sistemas UNIX). Utilizando las ventajas de la memoria virtual, se marcan una serie de páginas de memoria abarcando el tamaño del archivo de forma que apuntan a las partes del disco duro donde éste se halla. El programador escribirá o leerá diréctamente de estas zonas de memoria como si lo hiciera del fichero; cuando los datos solicitados no se encuentren en memoria (lo cual sucede siempre que se accede a una zona del archivo por primera vez) se generará un fallo de página y estos serán cargados desde el disco duro, modificándose o leyéndose aquello que haya sido solicitado por el programa. Cuando el fichero se cierra, se guardarán los cambios realizados en memoria.

Es evidente que el método de acceso mediante mapeado en memoria es mucho más cómodo dado que no hay que llamar a funciones que cambien el puntero de lugar, funciones que escriban y funciones que lean, sino que basta con escribir y leer en una zona de memoria accediendo diréctamente a todos los contenidos del fichero.

 

 

2.6.3.- Acceso a directorios

Las funciones para leer de un directorio varían según el sistema operativo; hay que reconocer aquí que a bajo nivel (ensamblador) las funciones de las que están dotados los sistemas Windows son bastante más sencillas que las de un Linux:

- Sistema tipo-Windows: Leer datos de un directorio es tan sencillo como usar las funciones FindFirst y FindNext que proporcionan el primer y siguientes archivos coincidentes con el patrón dado por el usuario. Podríamos por ejemplo indicar que queremos buscar "*.exe" (todos los ficheros con extensión exe), y con la primera llamada a FindFirst y las siguientes iríamos obteniéndolos todos.

- Sistema tipo-Linux: Tenemos una estructura interna llamada Dirent, entrada de directorio, a la que accederemos mediante la función ReadDir. El problema de esta función es la mala documentación presente a este respecto, lo que hace algo más difícil su manejo (readdir hace una lectura secuencial de cada entrada del directorio, sea esta fichero, directorio, etc). Personalmente, para poder utilizarla tuve que echarle un vistazo al código fuente de Linux para ver cómo funcionaba realmente esta estructura Dirent de cara a la programación en ensamblador.

 

 

 

2.7.- Procesos

 

2.7.1.- Concepto de un proceso

Podemos definir un proceso como un programa en ejecución; el objetivo de un sistema operativo es al fin y al cabo crear, ejecutar y destruir procesos de acuerdo a los deseos de los usuarios, por tanto es este un concepto fundamental en el estudio de los SSOO; podemos definir entonces proceso también como la unidad de procesamiento gestionada por el SO.

En cada procesador van a mantenerse una serie de estructuras de información que permitan identificar sus características y los recursos que tiene asignados. Una buena parte de ellas van a estar en el Bloque de Control de Proceso o BCP, que contiene (en el ejemplo tipo UNIX) información como la siguiente:

- Identificador de proceso: Se trata del pid, un número único que etiqueta al proceso para no ser confundido con ningún otro (también se va a almacenar el pid del proceso padre, es decir, del que creo a éste).

- Identificador de usuario: Conocido también como uid, identifica de forma inequívoca al usuario que inició el proceso.

- Estado de los registros: Situación actual de los registros del procesador (útil en la multitarea puesto que al volver a la ejecución de un proceso que se había dejado de atender, han de reponerse estos registros para seguir sin problemas).

- Descriptores: De ficheros abiertos indicando los que está manejando el fichero, de segmentos de memoria asignados y de puertos de comunicación abiertos.

Podemos resumir por tanto un proceso como un conjunto que abarca por un lado los segmentos de memoria en los que residen código y datos del proceso (imagen de memoria o core image), por otro lado el contenido de los registros del modelo de programación y finalmente el BCP ya mencionado.

 

 

2.7.2.- Jerarquía de procesos

Existe un proceso de inicio original a partir del cual se crean los demás; podríamos entonces describir el listado de procesos como un árbol en el que un proceso originario da lugar a otros que a su vez crean más. Cuando un proceso A solicita que al sistema operativo la creación de otro proceso B, se dirá entonces que A es padre del proceso B, y que B es hijo del A (y el padre podrá si lo desea matar a su proceso hijo).

 

 

2.7.3.- Multitarea

Dependiendo de las tareas y usuarios simultáneos, los sistemas operativos pueden ser:

- Monotarea: También monoproceso, ya que sólo permiten que haya un proceso en cada momento. Un ejemplo típico de esto sería Ms-Dos.

- Multitarea: O multiproceso, permitiendo que existan varios procesos activos al mismo tiempo, encargándose el sistema operativo de repartir el tiempo de procesador entre ellos.

- Monousuario: Sólo un usario puede ser soportado a la vez, pudiendo no obstante ser mono o multiproceso.

- Multiusuario: En ellos, el sistema operativo soporta varios usuarios a la vez en distintos terminales; un sistema así, es obligatoriamente multitarea.

La multitarea, se basa en el hecho de que en todo proceso que ejecutemos siempre van a haber espacios en los que el microprocesador no tiene nada que hacer; así, cuando se esté esperando una operación de lectura del disco duro el procesador no estará haciendo nada útil con lo que su tiempo puede ser utilizado para otras tareas. Así pues, tenemos un modelo más eficiente para la multitarea que simplemente asignar un trozo de tiempo a cada proceso; podemos adaptarnos a su funcionamiento usando sus tiempos de uso de la E/S en ejecutar otros procesos.

 

 

2.7.4.- Ejecución de un programa y formación de un proceso

Un fichero ejecutable va normalmente a mantener una estructura que va a incluir las siguientes partes:

- Cabecera: Contiene información acerca del ejecutable, como el estado inicial de los registros, descripciones con tamaño y localización de código y datos en el archivo, lugar de inicio de la ejecución del programa, etc.

- Código: Aquello que al ejecutar el proceso va a ser ejecutado por el sistema operativo; habrá un punto de comienzo o entry point que es donde se empieza a ejecutar.

- Datos: Existen de dos tipos; los datos inicializados por un lado van a ocupar espacio en disco, se trata de datos que poseen valor desde un principio. Los datos sin inicializar no ocupan espacio en el fichero, sino que va a reservárseles una porción de memoria para que el programa pueda utilizarlos (serán inicializados desde el propio código).

- Tabla de importaciones: Hará referencia a aquellas funciones que el ejecutable necesita de las librerías del sistema operativo. El motivo de no incluir estas funciones en el propio código es sencillo; si hay 100 ejecutables que usan la misma función es mucho más eficiente cargar la librería que las contiene cuando uno de ellos es ejecutado que almacenar el código de esta función en el código de cada ejecutable. En este caso habremos usado 100 veces menos espacio, puesto que la librería sólo tiene que estar en el disco duro una vez para que pueda ser utilizada por los ejecutables.

Así pues, cuando se inicie un proceso el sistema operativo asignará un espacio de memoria para albergarlo, seleccionará un BCP libre de la tabla de procesos rellenándolo con el pid y uid, descripción de memoria asignada y demás, y finalmente cargará en el segmento de código en memoria el código y las rutinas necesarias de sistema, y los datos en el segmento de datos contenido en el fichero, comenzando a ejecutar en su punto inicial. Además, el proceso recién iniciado tendrá una serie de variables propias que se pasan al proceso en el momento de su creación como puedan ser en UNIX algunas como PATH, TERM o HOME, además de los parámetros diréctamente pasados a través del shell.

 

 

 

2.8.- Seguridad

Existen dos grandes mecanismos para proteger la información y evitar el acceso por parte de usuarios a recursos para los que no han sido habilitados. Podríamos dividirlos en dos tipos; por un lado los que se llevan a cabo por parte del procesador, y por otro los que dependen del sistema operativo.

 

 

2.8.1.- Rings del procesador

En un microprocesador como el 80x86, es decir, el PC común, existen cuatro modos diferentes de ejecución o rings, de los que sólo se utilizan dos (el 0 y el 3). El ring0 es el modo privilegiado de ejecución, mientras que el ring3 es el modo de usuario. Cuando el procesador se ejecuta en modo supervisor o ring0, puede acceder a la zona del kernel, escribir sobre ella si la desea, controlar cualquier proceso... sin embargo en ring3 o modo usuario hay una gran cantidad de limitaciones que evitan que se pueda acceder a zonas no permitidas del sistema.

Por ejemplo, un micro ejecutándose en modo usuario no podrá modificar en memoria el código base del sistema operativo ni realizar varias operaciones prohibidas, como acceder diréctamente a los periféricos (este modo es en el que normalmente estamos ejecutando). Cuando queramos realizar una lectura del disco duro entonces lo que haremos será llamar a una interrupción pidiendo ese servicio. ¿Qué hará entonces el sistema operativo?. Dará control a la zona del kernel dedicada a tratar esa interrupción (y por tanto en esta ocasión de leer del disco duro), pasando a modo supervisor para que se pueda realizar su función. Al terminar el procesamiento de la interrupción, el micro volverá al estado de usuario y continuará ejecutando el programa; se trata de un método infalible si está bien implementado, para que el usuario del ordenador jamás pueda ser supervisor excepto en aquellas ocasiones que el sistema operativo lo permita.

 

 

2.8.2.- Protección de usuarios y ficheros

Algunos sistemas operativos permiten el acceso a distintos usuarios mediante un mecanismo llamado autenticación, que se puede basar en cosas que conoce el usuario (preguntar por un password, por ejemplo) o mediante técnicas más complejas como tarjetas de identificación o biometría (reconocimiento de una característica física del usuario como su huella dactilar, pupila, etc).

Este sistema adquiere mucha potencia cuando se suma a un sistema fuerte de protección de ficheros; en los sistemas UNIX, cada fichero tiene un dueño y una serie de permisos de acceso que consisten en 9 bits: "rwx rwx rwx". El primer bloque de tres bits se refiere al usuario que es dueño del fichero (r es read, lectura, w es write, escritura, y x es exec, ejecución), el segundo bloque al grupo de usuarios al que pertenece el dueño del fichero y el tercero al resto del mundo. Así, supongamos este fichero:

 

datos.txt: < rw- r-- --- > Usuario: Tifaret, Grupo: Arcontes

El archivo datos.txt pertenecería entonces al usuario Tifaret, el cual tendrá derechos de lectura y escritura sobre él. Todos aquellos usuarios pertenecientes a su mismo grupo de usuarios (Arcontes), tendrán permiso de lectura. Finalmente, el resto de usuarios del sistema no podrá acceder a este fichero.

Diferente es cuando estos permisos se aplican sobre un directorio; en este caso siguen habiendo nueve bits pertenecientes a usuario, grupo y resto de usuarios, pero la r especifica que el directorio se puede leer (hacer un listado de sus contenidos), la w que se puede añadir o borrar ficheros en él, y la x que se puede atravesar para acceder a ficheros alojados a partir de él.

Black Hole  
   
Facebook botón-like  
 
 
Hoy hay 9 visitantes¡Aqui en esta página!
Este sitio web fue creado de forma gratuita con PaginaWebGratis.es. ¿Quieres también tu sitio web propio?
Registrarse gratis