Firewall: controlando el acceso a la red.

Anuncio
Departament d’Enginyeria Informàtica i M atemàtiques
Firewall: controlando el acceso a la red.
TITULACIÓN: Ingeniería Técnica Informática de Sistemas
AUTORS: Jairo de la Fuente Vilaltella.
DIRECTORS: Carlos Molina Clemente
FECHA : Abril / 2011
[Firewall: controlando el acceso a la red.]
Índice
0. Prefacio……………………………………………………………..……………p. 6
1. Introducción……………………………………………………………………...p. 9
1.1 Seguridad informática…………………………………………………………p. 10
1.2 Amenazas actuales…………………………………………….………………p. 11
1.3 Porque proteger un ordenador personal...............................................................p. 12
1.4 Firewalls……………………………………………………....………………..p. 13
2. Objetivos……………………………………………..…………………………...p.15
2.1 Control de conexiones………………………………...………………………..p. 16
2.1.1 Crear un driver…………………………………………...………………....p. 16
2.1.2 Filtro………………………………………………………………...............p.16
2.2 Control por parte del usuario………………………………………...…………p. 17
2.2.1 Comunicación con el driver……………………………………...…………p. 17
2.2.2 Sincronismo…………………………………………………...……………p. 17
2
[Firewall: controlando el acceso a la red.]
3. Especificaciones…………………………………………………………………..p. 18
3.1 Componentes de la solución……………………………………………………p. 19
3.1.1 Driver………………………………………………………………………..p. 19
3.1.1.1 Acciones del driver……………….……………………………………..p. 19
3.1.1.1.1 Comunicación…………………………………………………..……p. 19
3.1.1.1.2Lectura………………………………………………………………p. 19
3.1.1.1.3 Escritura………………………...…………………………………..p. 20
3.1.1.2 Filtro………………………….………………………………………..p. 20
3.1.2 Programa de control……………………………...………………………….p. 20
3.1.2.1 Sincronismo…………………………………..………………………….p. 20
3.1.2.1.1 Creación de eventos…………………………………………………..p. 20
3.1.2.2Comunicación………………………………………….…………………p. 21
3.1.2.2.1 Informar al usuario…………………….……………………………..p. 21
3.1.2.2.2 Dar órdenes al driver………………………..………………………..p. 21
3.1.3 Lanzador………………………………………...…………………………..p. 21
3.1.3.1 Cargar el programa de control…………………..……………………….p. 21
3.1.3.2 Cargar el driver…………………………………………………………..p. 21
4. Diseño y desarrollo………………………………………………………………..p. 22
4.1 El Kernel de Windows…………………………………………………………p. 23
4.1.1 Modo kernel y modo Usuario…………………….…………………………p. 23
4.1.1.1 Drivers…………………………………………………………………..p. 25
4.1.1.1.1 API para drivers…………………………..…………………………p. 26
4.1.1.2 Memoria protegida y Hooks…………………………………………….p. 26
4.1.1.2.1 Modificar memoria protegida……………………….………………p. 27
3
[Firewall: controlando el acceso a la red.]
4.1.1.2.2 Suplantación de funciones del sistema (hook)……………………………p. 28
4.1.1.2.2.1 Creación de un filtro………………………………….…………..p. 29
4.1.1.2.2.1.1 Substitución del Major original……….………………………p. 29
4.1.1.2.2.1.2 Contexto de la llamada………………………………………..p. 31
4.1.1.2.2.1.3 Obtener información de los parámetros de la llamada………..p. 32
4.1.1.2.2.1.4 Averiguar quien llama a la función………………………...…p. 33
4.1.1.2.2.1.4.1 Llamadas a API's no documentadas ni definidas…….…….p. 33
4.1.1.2.2.2 Listas encadenadas en Drivers…………………………………....p. 35
4.1.1.2.2.2.1 Reserva de memoria…………………………………………..p. 35
4.1.1.2.2.2.2 Mutexs…………………………………………………….…..p. 36
4.1.1.2.2.3 Cola de trabajo……………………………………………………p. 37
4.1.1.2.2.4 Cola de reglas definidas…………………………………….…….p. 37
4.1.1.2.2.5 Tabla hashing……………………………………………..……p. 38
4.1.1.3 Comunicación entre Modo kernel y modo usuario………………...…..p. 39
4.1.1.3.1 Creación de dispositivos………………………………………….p. 39
4.1.1.3.1.1 Estructura de un dispositivo……………………………….….p. 40
4.1.1.3.1.1.1 Lectura…………………………………………………….....p. 41
4.1.1.3.1.1.2 Escritura…………………………………………………...p. 42
4.1.1.3.2 Sincronismo…………………………………………………………p. 42
4.1.1.3.2.1 Creación de eventos……………………………………………..p. 43
4.2 Esquema del conjunto……………………………………………………..…..p. 44
5. Evaluación y juego de pruebas…………………………………………………....p. 48
4
[Firewall: controlando el acceso a la red.]
6. Recursos usados …………………………………………………………………..p. 51
6.1 Bibliografía……………………………..………………………………………p. 52
6.2 Páginas web…………………………………………………………………….p. 53
6.3 Software…………………………………………………...……………………p. 53
6.3.1 Visual studio 2010………………………………………………….……….p. 53
6.3.2 Windows Drivers Kit………………………………………………………..p. 54
6.3.3 WinDbg…………………………………………………………………..…p. 54
6.3.4 DbgView…………………………………………………………………….p. 54
7. Conclusiones……………………………………..………………………………..p. 55
7.1 Comparación con otros productos…………………………...…………………p. 55
7.2 Posibles mejoras…………………………………………………………….….p. 56
7.3 Como proseguiría con el proyecto…………………………………………...…p. 56
8. Planificación temporal…………………………………………………………..p. 58
9. Manual de uso……………………………………………………….…………….p. 61
10. Anexo: Partes del código fuente……………………..…………………………..p. 63
5
[Firewall: controlando el acceso a la red.]
PREFACIO
6
[Firewall: controlando el acceso a la red.]
En este proyecto se encontrará el diseño y desarrollo de un programa que intenta dar
una respuesta a las necesidades actuales de seguridad en un ordenador personal.
Es evidente que internet ha marcado una auténtica revolución en los sistemas de
comunicación, constituyendo por su extensión y crecimiento exponencial, un fenómeno
social de primera magnitud a nivel mundial. Internet, se puede considerar como una red
de redes de ordenadores que se encuentran interconectados a modo de superautopistas
de la información, lo que permite comunicarnos fácilmente de forma rápida y directa
con cualquier otra persona del planeta que esté conectada al sistema. Para el mundo
científico y profesional, internet es una herramienta de extraordinaria importancia, solo
comparable a la aparición de la imprenta. Es, además, un instrumento tecnológico de
uso muy frecuente por parte de niños y jóvenes de todas las edades; las mayoría de las
veces como elemento de ocio, y, en ocasiones como método de ayuda para la
adquisición de datos y conocimientos académicos.
Todas las funcionalidades de Internet (navegación por las páginas web, publicación de
weblogs y webs, correo electrónico, mensajería instantánea, foros, chats, gestiones y
comercio electrónico, entornos para el ocio...) pueden comportar algún riesgo, al igual
como ocurre en las actividades que realizamos en el "mundo físico".
Al facilitar información personal y códigos secretos de diversos tipos de cuentas,
pueden ser interceptados por ciberladrones y pueden ser usadas para suplantar la
personalidad de sus propietarios y realizar incluso compras a su cargo si se ha obtenido
números bancarios o cuantas de pago electrónico. Con todo, se van desarrollando
sistemas de seguridad (firmas electrónicas, certificados digitales...) que cada vez
aseguran más la confidencialidad al enviar los datos personales necesarios para realizar
las transacciones económicas. Hay empresas que delinquen vendiendo los datos
personales de sus clientes a otras empresas y estafadores.
A través de mecanismos como troyanos, spyware o keyloggers se puede conocer todo
lo que se hace desde un ordenador, copiar todos los archivos que tiene almacenados un
usuario y recabar información confidencial.
En la red cada día surgen nuevas amenazas a las que se ven expuestos los usuarios.
7
[Firewall: controlando el acceso a la red.]
Este proyecto parte de este ambiente como motivación para realizar un estudio sobre
cómo elaborar un sistema de protección que detecte y pueda bloquear las conexiones
realizadas por programas en un ordenador personal, actuando así como primera línea
defensiva.
8
[Firewall: controlando el acceso a la red.]
Capítulo 1
Introducción
9
[Firewall: controlando el acceso a la red.]
1. Introducción
1.1 Seguridad Informática
La seguridad informática es una disciplina que se relaciona a diversas
técnicas, aplicaciones y dispositivos encargados de asegurar la integridad y privacidad
de la información de un sistema informático y sus usuarios.
Técnicamente es imposible lograr un sistema informático ciento por ciento seguros,
pero buenas medidas de seguridad evitan daños y problemas que pueden
ocasionar intrusos.
Si bien es cierto que todos los componentes de un sistema informático están expuestos
a un ataque (hardware, software y datos) son los datos y la información los sujetos
principales de protección de las técnicas de seguridad. La seguridad informática se
dedica
principalmente
a
proteger
la confidencialidad,
la integridad y disponibilidad de la información.
Confidencialidad
La confidencialidad se refiere a que la información solo puede ser conocida por
individuos autorizados. Existen infinidad de posibles ataques contra la privacidad,
especialmente en la comunicación de los datos. La transmisión a través de un medio
presenta múltiples oportunidades para ser interceptada y copiada: las líneas
"pinchadas" la intercepción o recepción electromagnética no autorizada o la simple
intrusión directa en los equipos donde la información está físicamente almacenada.
Integridad
La integridad se refiere a la seguridad de que una información no ha sido alterada,
borrada, reordenada, copiada, etc., bien durante el proceso de transmisión o en su
propio equipo de origen. Es un riesgo común que el atacante al no poder descifrar un
paquete de información y, sabiendo que es importante, simplemente lo intercepte y
lo borre.
10
[Firewall: controlando el acceso a la red.]
Disponibilidad
La disponibilidad de la información se refiere a la seguridad que la información
pueda ser recuperada en el momento que se necesite, esto es, evitar su pérdida o
bloqueo, bien sea por ataque doloso, mala operación accidental o situaciones
fortuitas o de fuerza mayor.
1.2 Amenazas Actuales
Malware
Por malware se
entiende cualquier programa de software que
se instala
inadvertidamente y de forma discreta en el equipo de un usuario y ejecuta acciones
inesperadas o no autorizadas, pero siempre con intenciones maliciosas. Es un término
genérico utilizado para referirse a virus, troyanos y gusanos.
Virus
Es un programa o software que se autoejecuta y se propaga insertando copias de sí
mismo en otro programa o documento.
Un virus informático se adjunta a un programa o archivo de forma que pueda
propagarse, infectando los ordenadores a medida que viaja de un ordenador a otro.
Gusanos
Los gusanos informáticos se propagan de ordenador a ordenador, pero a diferencia de
un virus, tiene la capacidad a propagarse sin la ayuda de una persona. Un gusano
informático se aprovecha de un archivo o de características de transporte de tu sistema,
para viajar.
Lo más peligroso de los worms o gusanos informáticos es su capacidad para replicarse
en tu sistema, por lo que tu ordenador podría enviar cientos o miles de copias de sí
mismo, creando un efecto devastador enorme. Un ejemplo sería el envío de una copia
11
[Firewall: controlando el acceso a la red.]
de sí mismo a cada uno de los contactos de tu libreta de direcciones de tu programa de
email. Entonces, el gusano se replica y se envía a cada uno de los contactos de la libreta
de direcciones de cada uno de los receptores, y así continuamente.
Troyanos
Crean puertas traseras o backdoors en tu ordenador permitiendo el acceso de usuarios
malévolo a tu sistema, accediendo a tu información confidencial o personal.
A diferencia de los virus y gusanos, los troyanos ni se auto replican ni se reproducen
infectando otros archivos.
Spyware
El spyware es cualquier pieza de software, instalado o utilizado sin su conocimiento,
que vigila, registra e informa de sus movimientos electrónicos. El spyware comercial
vende la información que recopila a empresas de publicidad; el spyware utiliza esta
información con fines delictivos para robar la identidad de los usuarios; se trata de la
amenaza más grave a la que deben hacer frente los usuarios.
Crimeware
Es un término general que describe un tipo de software que se utiliza para el hurto de
datos financieros. El crimeware se puede propagar prácticamente por medio de
cualquier vector de amenaza, incluidos virus/troyanos/gusanos,spyware/adware, entre
otros programas, y está formado por programas robot, redes robot y ransomwar
1.3 Porque Proteger un Ordenador Personal
Cuando hablamos de seguridad informática, muchas veces hacemos caso omiso a los
problemas que nos pueden ocasionar el no estar bien protegidos, pero hasta el momento
que nos sucede ahí es cuando nos arrepentimos de no haber tomado las medidas
necesarias con anterioridad, creo que la clave de la seguridad informática esta y
comienza en nosotros mismos usando solo nuestro sentido común.
Lo primero que tenemos que tomar en cuenta antes de tomar precauciones es
12
[Firewall: controlando el acceso a la red.]
precisamente conocer de que nos estamos previniendo, debemos saber de qué nos
estamos cuidando antes de actuar. Los daños en nuestro ordenador pueden ser muy
diferentes dependiendo de la amenaza o virus que tengamos residente en nuestro
equipo.
Los usuarios cada vez dependen más de los sistemas informáticos como medios para
almacenar datos, trabajos, información personal, comunicarse entre ellos y realizar
compras por internet, lo que supone una gran cantidad de información privada y
susceptible de ser substraída para diversos fines, como usar cuentas de paypal ajenas
con las consiguientes consecuencias.
1.4 Firewalls
El firewall es un programa que actúa a modo de filtro, bloqueando o permitiendo
determinadas conexiones y transmisiones de datos desde o hacia Internet, es decir, actúa
como una barrera entre la red y nuestro PC. Para entender esta definición podemos
observar la figura:
Figura 1. Firewall
13
[Firewall: controlando el acceso a la red.]
Como lo dice su traducción, no es otra cosa que un cortafuegos, acepción adaptada de la
terminología de los servicios contra incendios, que cuentan con una técnica especial
para poder controlar fuegos de gran intensidad y evitar su propagación.
Un firewal en informática es muy similar porque permite controlar los accesos no
deseados mediante políticas de seguridad que pueden ser definidas por el usuario, tanto
a nivel local, como prestando servicios en red.
Para ello existen dos modalidades de aplicaciones Firewall:
•
Aquellas diseñadas para empresas y para ser instaladas en proxys (equipos que
sirven como punto de conexión para otros conectados en red).
•
Las destinadas a los usuarios individuales, que será el tipo de firewall tratado en
este proyecto. Este tipo de Firewall te avisa cuando algún programa trata de
conectarse a internet desde tu PC, intentando de esta forma proteger el
ordenador de posibles intrusiones de Internet. Últimamente debido a la gran
cantidad de programas espía que existen, el uso de un firewall se ha vuelto
indispensable.
De esta manera un usuario puede bloquear programas que ni si quiera se conoce
su residencia en el ordenador debido a técnicas de ocultación y no solo bloquear
su conexión a internet evitando así una fuga de información sino también
descubriendo su existencia en el sistema.
14
[Firewall: controlando el acceso a la red.]
Capítulo 2
Objetivos del Proyecto
15
[Firewall: controlando el acceso a la red.]
2. Objetivos del Proyecto
El resultado que se espera alcanzar serán los siguientes.
Lo que se pretende conseguir con este proyecto es un control por parte del usuario de las
conexiones a la red de los programas.
2.1 Control de Conexiones
Se propone conseguir es una aplicación (firewall) que regule los accesos a la capa de
red de las aplicaciones que lo soliciten.
El sistema operativo escogido es Windows 7, ya que es uno de los sistemas operativos
con más recepción de malware y el SO más común en los hogares y centros de docencia
españoles.
Para este menester será necesaria la creación de un driver que filtre las conexiones.
2.1.1 Crear un Driver
Para tal cometido se creará un driver, un programa que formará parte del núcleo del
sistema operativo y permitirá trabajar a bajo nivel pudiendo obtener el control de ciertas
partes del sistema.
2.1.2 Filtro
Para acceder a la capa de red será necesario pasar por un filtro que decidirá si la
aplicación puede acceder a la capa o no, manteniendo una lista de los procesos que
pueden acceder y cuáles no.
16
[Firewall: controlando el acceso a la red.]
2.2 Control por Parte del Usuario
Las decisiones del filtro de permitir o denegar el acceso a internet a una determinada
aplicación serán tomas por el usuario, por eso mismo será necesario una comunicación y
sincronismo entre el usuario y el driver.
2.2.1 Comunicación con el Driver
Para dotar al usuario del control del filtro se implementara una infraestructura que
permitirá la comunicación bidireccional desde el núcleo del sistema y la interfaz de
usuario.
2.2.2 Sincronismo
El filtro y las decisiones tomadas por el usuario estaran plenamente sincronizadas
asincronamente trabajando asi cada parte por separado a su frecuencia de trabajo sin
afectar el rendimiento del sistema ni esperas innecesarias.
17
[Firewall: controlando el acceso a la red.]
Capítulo 3
Especificaciones del Proyecto
18
[Firewall: controlando el acceso a la red.]
3. Especificaciones
En este apartado se pretende especificar las funciones que deberá tener el proyecto para
realizar su cometido.
3.1 Componentes de la Solución
El proyecto se dividirá en tres componentes necesarios para obtener su solución: un
driver, un programa de control por parte del usuario y un lanzador del conjunto.
3.1.1 Driver
El driver permitirá obtener privilegios de system, los más altos en un ordenador
quitando cualquier barrera que impida modificar la totalidad del sistema, necesarios
para el fin que se intenta alcanzar.
3.1.1.1
Acciones del Driver
Se diferencian dos grandes objetivos por parte del driver: el filtraje a la capa de red y la
comunicación con el usuario para definir reglas y accesos.
3.1.1.1.1
Comunicación
Quien definirá las políticas de acceso a internet para cada programa de forma individual
será el usuario, por lo tanto se necesita una infraestructura que permita la comunicación
en entre el driver y éste. Mientras se espera la respuesta del usuario la política por
defecto empleada será restrictiva, evitando así fugas de información.
El driver creara un dispositivo sobre el cual se podrán hacer lecturas y escrituras.
3.1.1.1.1.2 Lectura
El driver contendrá un buffer con la información que se pretende transmitir al usuario y
se podrá obtener dicha información mediante una lectura al dispositivo.
19
[Firewall: controlando el acceso a la red.]
3.1.1.1.2
Escritura
De la misma manera que en la lectura, el driver contendrá un buffer donde el usuario
podrá dar órdenes al driver mediante. Todo esto mediante la escritura de los datos en el
dispositivo creado para tal fin.
3.1.1.2
Filtro
El driver suplantará la capa de red para interponerse entre ella y los programas que
deseen acceder a ella. Para ello, suplantará en memoria el driver original de acceso a la
red, llamado TCPIP.sys, por la función filtro del driver del proyecto, pudiendo después
pasar la llamada al driver original o por el contrario denegando el acceso.
3.1.2
Programa de Control
Este programa será el encargado de interactuar con el usuario y el driver corriendo en el
contexto de usuario, no el de núcleo del sistema, tal y como en el driver. Para una
comunicación con el usuario serán necesarios métodos de sincronización entre las
respuestas del usuario y el driver.
3.1.2.1
Sincronismo
Debido a la diferente frecuencia de trabajo entre las aplicaciones cpu y usuario, toda la
comunicación y establecimiento de permisos será realizado de una manera asíncrona,
así que serán necesarios sistemas de sincronización entre el driver y la aplicación de
usuario. Para ello se implementaran eventos.
3.1.2.1.1
Creación de Eventos
Para sincronizar la aplicación de usuario y el driver, que correrán en contextos de
ejecución y privilegios diferentes, recordemos que el driver formará parte del núcleo del
sistema operativo y la aplicación de usuario se ejecutará como un usuario administrador.
Será necesario la creación de eventos para una sincronización del sistema firewall.
20
[Firewall: controlando el acceso a la red.]
3.1.2.2
Comunicación
Para la comunicación con el driver se implementaran lecturas y escrituras al dispositivo
creado por el driver para informar y recibir órdenes del usuario.
3.1.2.2.1
Informar al Usuario
Mediante la lectura del dispositivo se tratará la información que el driver quiera
comunicar al usuario. Se sabrá cuando leer por el sincronismo entre las dos partes.
3.1.2.2.2
Dar Órdenes al Driver
Mediante la escritura del dispositivo que crea el driver se permite darle órdenes, que
serán las encargadas de definir las políticas de acceso para cada programa.
3.1.3
Lanzador
Será el programa que se ejecute para iniciar el firewall y se encargará de poner en
funcionamiento las diversas partes.
3.1.3.1
Cargar el Programa de Control
Primero de todo, se ejecutara el programa de control del usuario que
iniciará los mecanismos de sincronización, para después pasar a cargar el driver.
3.1.3.2 Cargar el Driver
Para inicializar el driver en el sistema, antes de todo se registrará el driver como servicio
y después se inicializara como servicio activo.
21
[Firewall: controlando el acceso a la red.]
Capítulo 4
Diseño y Desarrollo
22
[Firewall: controlando el acceso a la red.]
4. Diseño y Desarrollo
En el diseño y desarrollo se explicaran los conocimientos, técnicas e implementaciones
usadas para resolver el problema que se plantea.
4.1
El Kernel de Windows
En informática, un núcleo o kernel es un software que actúa de sistema operativo.
Es
el
principal
responsable
de
facilitar
a
los
distintos
programas acceso
seguro al hardware del ordenador o en forma más básica. Es el encargado de gestionar
recursos, a través de servicios de llamada al sistema. Como hay muchos programas y el
acceso al hardware es limitado, también se encarga de decidir qué programa podrá hacer
uso de un dispositivo de hardware y durante cuánto tiempo, lo que se conoce
como multiplexado.
Acceder al hardware directamente puede ser realmente complejo, por lo que los núcleos
suelen implementar una serie de abstracciones del hardware. Esto permite esconder la
complejidad, y proporciona una interfaz limpia y uniforme al hardware subyacente, lo
que facilita su uso al programador. Las partes formadas por el núcleo son las que corren
en privilegios de kernel y los programas ajenos al núcleo que llaman a las abstracciones
son los que corren en el modo usuario.
4.1.1 Modo Kernel y Modo Usuario
Los privilegios de un sistema operativo se dividen en los siguientes:
Figura 2. Anillos
23
[Firewall: controlando el acceso a la red.]
Los Ring's (Anillos) de la imagen, son privilege rings (anillos de privilegios). Los que
tienen más privilegios están en el corazón (Ring0), y los que menos, al exterior (Ring3).
Las aplicaciones que ejecutamos en nuestro PC están todas en Ring3, aunque hay
algunas que tienen una parte en Ring3 y otra en Ring0 (Anti-Virus, Firewalls). Esta
mezcla entre modo usuario y modo kernel es debida a que el modo usuario está muy
limitado en cuanto a privilegios.
El firewall se compondrá de dos componentes, uno para cada nivel de privilegios: el
driver, que será el que corra en nivel Ring 0 y la aplicación de usuario, que correrá en
modo usuario(Ring3).
El kernel de Windows incorpora dentro del modo usuario un Subsistema de Ambiente
Protegido. Esto significa que controla los errores en los procesos, lo que a su vez
implica que si se produce un error en algún programa, éste puede ser descargado de
memoria sin provocar en el sistema operativo ningún tipo de error (dependiendo de qué
aplicación tenga el error, evidentemente). En modo Kernel no existe esta protección, lo
que provoca que si un driver tiene un error, el sistema operativo se ve obligado a
reiniciar el PC.
Los procesos que se ejecutan dentro de modo usuario y poseen memoria virtual. Por lo
tanto, la posición de memoria 0x00457ab no es la misma posición físicamente. Esto lo
maneja el Kernel de Windows para impedir que otros procesos escriban o modifiquen
datos de otros procesos. Los drivers, en cambio, se ejecutan dentro de la memoria física,
lo que equivale a que es posible escribir en la memoria de otros drivers y en el mismo
kernel, aunque algunas páginas de memoria tengan protección de escritura (que se
puede eliminar).
24
[Firewall: controlando el acceso a la red.]
4.1.1.1
Drivers
La descripción que podemos encontrar sobre drivers podría ser esta:
Un driver o controlador de dispositivo para equipos con sistema operativo Windows, es
un programa cuya finalidad es relacionar el sistema operativo con los
dispositivos hardware (tarjeta gráfica, tarjeta de sonido, módem, tarjeta de Tv, wifi,
lector mp3, etc.) y periféricos (impresora, escáner, cámara fotográfica, cámara de
vídeo, etc.) de nuestro equipo.
Pero ese sería el objetivo general de un driver, aprovechando que un driver puede
modificar totalmente el sistema operativo, porque forma parte del kernel, se podría usar
para alterar la memoria del sistema, su estructura o añadir funciones al sistema que no
tengan que ver con la gestión directa de hardware. Un claro ejemplo de esto sería un
antivirus, un firewall, un control parental…etc.
Una vez se está en modo kernel, tenemos que pensar un poco diferente de cómo lo
haríamos en modo usuario. Tenemos que hacernos a la idea de que nuestro driver no es
un proceso. Los procesos en modo usuario tienen un espacio propio, definido, y
remarco el propio, ya que en modo kernel es algo diferente. Es cierto que nuestro driver
se aloja en una posición de memoria definida, pero al hacer llamadas a funciones,
tenemos que saber que podemos leer y escribir en toda la memoria (luego veremos que
en ciertas áreas se nos está restringido escribir, pero modificando un registro lo
podemos deshabilitar). Esto equivale a que si, por ejemplo, queremos modificar el
contenido de una de las varias tablas que residen en el kernel de Windows, lo podemos
hacer sin necesidad de llamar a ninguna API.
Como hemos visto en la figura 2, un driver corre en Ring0 y un contexto diferente al de
un programa en modo usuario, sus estructuras y requerimientos son distintos, por lo que
las APIs empleadas diferirán de un programa convencional.
API
(Application Program Interface). Conjunto de convenciones internacionales que
definen cómo debe invocarse una determinada función de un programa desde una
aplicación. Cuando se intenta estandarizar una plataforma, se estipulan unos APIs
comunes a los que deben ajustarse todos los desarrolladores de aplicaciones.
25
[Firewall: controlando el acceso a la red.]
4.1.1.1.1 API para Drivers
Windows estipula su propio elenco de APIs, que difiere mucho de las APIs a las que
podemos ver en programas de modo usuario.
Tomando como ejemplo el lenguaje C, donde podemos encontrar funciones como printf
y cabeceras típicas como windows.h, vemos que a la hora de programar drivers en C
quedan totalmente restringidas esas funciones y cabeceras.
Todas las cabeceras y funciones quedan substituidas por otras e interpretadas y
compiladas por un compilador especial para drivers.
La mayoría de las funciones no tienen un substituto natural en la API para drivers,
inclusive muchas cosas fácilmente implementables en modo usuario no son posibles en
un driver.
Existen unas tablas donde se guardan las direcciones de memoria a las APIs y demás
llamadas al sistema, estas tablas se encuentran en una memoria protegida.
4.1.1.2
Memoria Protegida y Hooks
La memoria en el sistema operativo está dividida por páginas, como un libro. Algunas
de estas páginas están protegidas por seguridad para que solamente sean de lectura. No
todas las estructuras que se puedan modificar están protegidas de este modo, ya que
algunas no se tiene que modificar nada y se pueden manipular directamente.
Un hook es, básicamente, substituir una función por otra. Por ejemplo, se podría
substituir la API MessageBox por una función programada por nosotros mismos para
que al llamar la API MessageBox, una aplicación, se ejecute nuestra función en vez de
la original.
Con esto se puedo modificar o filtrar llamadas al sistema.
26
[Firewall: controlando el acceso a la red.]
4.1.1.2.1
Modificar la Memoria Protegida
Las tablas donde se guardan las direcciones de memoria a las APIs del sistema y demás
funciones provistas por drivers, en este caso el driver TCPIP.sys, se encuentran en una
paginación protegida contra la escritura.
El objetivo es interponernos en las llamadas al driver TCPIP por tanto necesitaremos
modificar esas tablas.
El registro CR0 contiene un bit llamado write protect que es el encargado de señalar si
una página es de solo lectura o no. Para eliminar esta propiedad se tiene que poner a
cero este bit.
Para eso, se implementara un pequeño código en ensamblador dentro del driver que
desprotegerá la memoria y la volverá a proteger al acabar la modificación oportuna.
En seudocódigo:
Guardar EAX
EAX = CR0
EAX = EAX AND 0FFFEFFFh
CR0 = EAX
Para devolverlo a su estado original se procederá de la misma manera pero modificando
EAX de la siguiente manera:
EAX = EAX OR NOT 0FFFEFFFh
Anexo 1.
Desactivando la protección y pudiendo modificar las direcciones de las llamadas al
sistema se puede suplantar funciones y APIs por otras funciones.
27
[Firewall: controlando el acceso a la red.]
4.1.1.2.2
Suplantación de Funciones del Sistema (hook)
Para tal cometido tomaremos como supuesto que se quiere substituir la API
ZwOpenProcess, en la tabla SSDT (System Service Descriptor Table) encontrándonos
la tabla inicialmente de la siguiente forma:
Nombre de la función
Dirección
ZwOpenProcess
0xAC25698
MessageBox
0xB56516D
….
….
Lo que se pretende conseguir es modificar la dirección de ZwOpenProcess para que
apunte a una función creada por mí. Para ello, primero se deberá crear una función con
los mismos parámetros que la original, que devuelva el mismo tipo de resultado, sino se
pueden producir todo tipo de errores y cuelgues en el sistema.
Una vez preparada la función substituta con las acciones que se pretendan conseguir con
ella será el momento de obtener la dirección de la función substituta, desproteger la
memoria como se ha visto anteriormente y substituir la dirección de ZwOpenProcess
por la dirección donde se encuentra la nueva función.
Si la nueva función se encuentra en la posición de memoria 0x45EF465, tras modificar
la tabla quedaría así:
Nombre de la función
Dirección
ZwOpenProcess
0x45EF465
MessageBox
0xB56516D
….
….
A partir de entonces cualquier programa que llame a la API ZwOpenProcess, en verdad
estará ejecutando otra función.
Con esta técnica se puede crear un filtro en las llamadas al sistema.
28
[Firewall: controlando el acceso a la red.]
4.1.1.2.2.1
Creación de un Filtro
Se puede controlar el resultado de las llamadas a funciones de APIs he incluso
modificar o alterar sus resultados con las diversas técnicas de hooking.
Prosiguiendo con el ejemplo anterior de ZwOpenProcess, se podría filtrar los procesos
que son abiertos y evitar que ciertos procesos pudieran ser cerrados o modificados (para
cerrar o modificar un proceso antes hay que abrirlo con la API ZwOpenProcess o una
variante de ella que más tarde llamara a ZwOpenProcess). Ésto se podría conseguir de
la siguiente forma:
La función substituta se podría obtener mediante los parámetros que son pasados a la
función el PID (process identification number) del proceso que se quiere abrir y
compararlo con el PID del proceso que queremos proteger. Si coinciden se puede
devolver un ACCES_DENIED, por el contrario se devolvería el control a la API
ZwOpenProcess, llamándola mediante la dirección original almacenada en una variable,
pasándole los parámetros con los que ha sido invocada la función substituta.
De esta manera se consigue total transparencia y funcionalidad normal del sistema, pero
en realidad se está tomando el control de ciertas acciones permitiéndolas o
denegándolas.
Se usará esta técnica para obtener cierto control sobre la capa de red, pero en vez de
substituir
APIs
4.1.1.2.2.1.1
se
substituirá
un
Major
del
driver
TCIP.sys.
Substitución del Major Original
Un driver por definición y estructura contiene una serie de llamadas o eventos que
pueden ser invocados sobre él, llamados majors. Un ejemple de majors seria, por
ejemplo, IRP_MJ_READ y IRP_MJ_WRITE, que son eventos llamados cuando se
intenta leer o escribir en un dispositivo creado por el driver. El programador del driver,
si implementa código dentro de esos eventos en el driver puede realizar las tareas que
crea oportunas al llamar esos eventos. Más adelante se verá una implementación para
esos dos majors, que nos permitirá comunicarnos con el usuario.
29
[Firewall: controlando el acceso a la red.]
Existe una gran variedad de majors IRP_MJ_CREATE, IRP_MJ_WRITE_CLOSE,
IRP_MJ_DEVICE_CONTROL... El que será usado para el cometido del proyecto será
IRP_MJ_INTERNAL_DEVICE_CONTROL, este major es una llamada genérica de
entrada/salida donde el programador puede crear eventos propios del driver que
programa.
Cuando un programa intenta realizar alguna acción sobre la red como escuchar en un
puerto, conectarse a un puerto, enviar un datagrama, etc, se comunica con ese driver.
Por lo tanto el objetivo es interceptar ese major concreto.
Para substituir el major será necesario modificar el puntero que apunta a la función
original. Para ello se obtendrá un puntero al dispositivo del driver, para a través de
dicho puntero aconseguir otro al driver, con el fin de acceder al major deseado. El
puntero de la dirección original se guardará para devolver el sistema a su estado original
al descargar el driver o para realizar llamadas al major original.
Se procederá de la siguiente manera:
PDispositivo = ObtenerPunteroDispositivo(“\\Device\TCP”)
PDriver = PDispositivo->ObjetoDriver
IrpMajorOriginal=PDriver>MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL]
Con esto ya está localizada y guardada la direccion al major original. El siguiente paso
será intercambiar esa dirección por la dirección de una función en nuestro driver de la
siguiente manera:
DesprotegerMemoria()
IntercambiarDirecciones(PDriver>MajorFunction[IRP_MJ_INTERNAL_DEVICE_C
ONTROL], MiFuncion)
ProtegerMemoria()
En el anexo 2 se encuentra el código completo.
30
[Firewall: controlando el acceso a la red.]
Ahora será necesario crear la función MiFuncion que recibirá la llamada de los demás
programas y será ejecutado en el diversos contextos dependiendo de quien haga la
llamada.
4.1.1.2.2.1.2 Contexto de la Llamada
A partir de ahora hablaremos del diseño y desarrollo de la función substituta
(MiFuncion), que será la encargada de hacer la función de filtro permitiendo o
denegando el acceso a la capa de red.
Como nos situamos dentro del contexto y necesidades que tiene un proceso al llamar al
driver de red? Con I/O Request Packet.
El Abreviado IRP básicamente es una estructura que permite a diferentes drivers
comunicarse entre sí y requerir tareas a finalizar
El manejo de IRPs puede ser muy simple o demasiado complejo dependiendo de cuál
sea la estructura de los drivers.
El IRP también contendrá "peticiones secundarias", conocido como "IRP Stack
Location". Cada driver contendrá sus propias peticiones secundarias respecto de cómo
interpretar el IRP, denominada IO_STACK_LOCATION. La definición de la función
quedara así:
NTSTATUS HookedDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP
Irp)
Siendo DeviceObject el dispositivo al que se intenta acceder y Irp la estructura con los
datos necesarios para la llamada. El valor devuelto será ACCES_DENIED si no se
permite al proceso acceder a la red, por el contrario será llamada la función original y se
devolverá su resultado.
Ahora se verá como ver más detalladamente la información del Irp.
31
[Firewall: controlando el acceso a la red.]
4.1.1.2.2.1.3 Obtener Información de los Parámetros de la Llamada
Lo primero será situarse en la pila de entrada y salida de información del Irp mediante:
PIO_STACK_LOCATION IoStack=IoGetCurrentIrpStackLocation(Irp);
Después se podrá interpretar a que subrutina intenta accede el proceso mediante la
lectura del Minor Function y poder clasificar que tipo de acción pretende hacer el
proceso sobre la red.
switch(IoStack->MinorFunction)
{
case TDI_SEND:
//Intenta enviar un paquete TCP
case TDI_SEND_DATAGRAM:
//Intenta enviar un paquete UDP
case TDI_CONNECT:
//Intenta realizar una conexión TCP
…
…
De esta manera se podrá determinar qué tipo de acción pretende hacer un programa
sobre la red, incluso definir reglas para cada tipo de acción, aunque en este proyecto se
usará a modo informativo.
32
[Firewall: controlando el acceso a la red.]
4.1.1.2.2.1.4 Averiguar Quien Llama a la Función
El objetivo de este proyecto es decidir qué programas pueden o no pueden acceder a la
red. Por lo tanto, en algún momento habrá que localizar quien está efectuando la
llamada al driver. Esto es relativamente fácil, ya que la función, al ser llamada por el
proceso, se ejecuta en el contexto del proceso pudiendo obtener el PID (process number
identification) de manera sencilla con la siguiente instrucción:
PID = PsGetCurrentProcessId();
Se ha creado una tabla de PIDs con los permisos de acceso sobre la capa de red.
Uno de los objetivos es que sólo se pregunte una vez al usuario si un programa puede
acceder o no a la red, así que no sólo será necesario guardar el PID del programa, sino
también su ruta y nombre para las siguientes ejecuciones del programa. Ésto ya no es
tan trivial, no se pone a disposición del programador ningún tipo de API para tal
cometido.
He optado por definir una API no definida ni documentada para tal propósito.
4.1.1.2.2.1.4.1 Llamadas a API's no Documentadas ni Definidas
Que una API no esté definida en las cabeceras incluidas en un compilador o un entreno
de desarrollo y que la msdn de Microsoft no la documente no significa que no esté
presente en el sistema, así que se realizará la definición y obtención de la API
ZwQueryInformationProcess desde el driver.
Para ellos habrá que definir un tipo de función con los parámetros necesarios:
typedef NTSTATUS (*QUERY_INFO_PROCESS) (
__in HANDLE ProcessHandle,
__in PROCESSINFOCLASS ProcessInformationClass,
__out_bcount(ProcessInformationLength) PVOID ProcessInformation,
__in ULONG ProcessInformationLength,
__out_opt PULONG ReturnLength
);
33
[Firewall: controlando el acceso a la red.]
Y definir el nombre de la función que representara al tipo creado:
QUERY_INFO_PROCESS ZwQueryInformationProcess;
Una vez definida la función con el tipo de parámetros necesarios será el momento de
obtener la dirección donde se aloja dicha API y asociarla con la función definida:
RtlInitUnicodeString(&routineName, L"ZwQueryInformationProcess");
ZwQueryInformationProcess = (QUERY_INFO_PROCESS)_
_MmGetSystemRoutineAddress(&routineName);
De esta manera ya se podra hacer un uso normal de la API.
Esta API pasándole el handle del proceso permite acceder a un conjunto de estructuras
de información del proceso que nos brindara la ruta y el nombre del programa.
hProcess ObtenerHandleProceso()
ZwQueryInformationProcess(hProcess, ProcessBasicInformation,
&ProcInfo,….)
myPEB = ProcInfo.EstructuraPEB
pupp = myPEB->ParametrosDelProceso
Después de recorrer estas estructuras se obtiene la ruta de la siguiente manera:
pupp->ImagePathName.Buffer
Con esta información se podrá definir listas de permisos para cada proceso.
34
[Firewall: controlando el acceso a la red.]
4.1.1.2.2.2
Listas Encadenadas en Drivers
Una lista encadenada es una de las estructuras de datos fundamentales, y puede ser
usada para implementar otras estructuras de datos. Consiste en una secuencia de nodos,
en los que se guardan campos de datos arbitrarios y una o dos referencias (punteros) al
nodo anterior o posterior. El principal beneficio de las listas enlazadas respecto a
los array convencionales es que el orden de los elementos enlazados puede ser diferente
al orden de almacenamiento en la memoria o el disco, permitiendo que el orden de
recorrido de la lista sea diferente al de almacenamiento.
Una lista enlazada es un tipo de dato auto-referenciado porque contienen un puntero o
link a otro dato del mismo tipo.
Otra de sus ventajas es la asignación de memoria dinámicamente, por cada nuevo nodo
se reserva una determinada cantidad de memoria.
4.1.1.2.2.2.1 Reserva de Memoria
Como se ha comentado anteriormente la función filtro se ejecutara desde diversos
contextos, eso podría ocasionar problemas a la hora de acceder a una lista encadenada.
La manera de evitar conflictos será mediante la colocación de esas listas en una porción
de memoria no paginada accesible desde cualquier contexto. A la hora de proceder a la
declaración de un nuevo nodo se actuara de la siguiente manera:
NuevoNodo = RserverMemoria(NoPaginada,TamañoNodo, ' Etiqueta');
*Anexo 3
La Etiqueta serviría en caso de error para poder debugear la memoria e identificar a que
está asociada una porción de memoria.
Esta lista será accedida desde diversos hilos de ejecución ya que la función filtro podrá
ser llamada desde diversos procesos a la vez lo que provocara un intento de acceso a
memoria compartida o una modificación simultáneamente, por lo tanto habrá que
establecer mecanismos de control de acceso. Los elegidos han sido los mutexs.
35
[Firewall: controlando el acceso a la red.]
4.1.1.2.2.2.2 Mutexs
Los algoritmos de exclusión mutua (comúnmente abreviada como mutex por mutual
exclusion) se usan en programación para evitar el uso simultáneo de recursos comunes,
como variables globales, conocidos como secciones críticas.
La mayor parte de estos recursos son las señales, contadores, colas y otros datos que se
emplean en la comunicación entre el código que se ejecuta cuando se da servicio a
una interrupción y el código que se ejecuta el resto del tiempo. Se trata de un problema
de vital importancia porque, si no se toman las precauciones debidas, una interrupción
puede ocurrir entre dos instrucciones cualesquiera del código normal y esto puede
provocar graves fallos.
La técnica que se emplea comúnmente para conseguir la exclusión mutua es inhabilitar
las interrupciones durante el conjunto de instrucciones más pequeño, que impedirá la
corrupción de la estructura compartida (la sección crítica). Esto impide que el código de
la interrupción se ejecute en mitad de la sección crítica.
Por lo tanto se definirán dos mutexs uno para cada una de las dos colas que contiene el
proyecto. Cuando se quiera acceder o modificar una cola el código de lectura y/o
modificación ira entre una abertura de la exclusión y terminará con el cierre de ella.
mutex1 = NuevoMutex()
ActibarMutex(mutex1)
//Tratamiento de la cola
DesactibarMutex(mutex1)
Existirán dos colas, la de trabajo y la de reglas definidas que serán protegidas de un
acceso simultaneo mediante los dos mutexs.
36
[Firewall: controlando el acceso a la red.]
4.1.1.2.2.3
Cola de Trabajo
Una de las listas encadenadas será una cola con la información a enviar al usuario.
La cola contendrá la información en espera que el usuario la trate. Esa información será
el nombre del programa que intenta acceder a la red, y la acción que intenta desarrollar
en ese momento se estará a la espera de que el usuario permita o no a esa aplicación
acceder a la red. En ese momento se pasara al siguiente nodo, en caso de no haber
siguiente nodo se esperara a que lo haya.
typedef struct _COLA_TRABAJO
{
char mensaje[500];
//Explicación de la acción que intenta hacer
char path[500];
//Ruta y nombre del programa
HANDLE pid;
//PID del proceso
struct _COLA_TRABAJO *siguiente;
//Puntero al siguiente nodo
} COLA_TRABAJO, *PCOLA_TRABAJO;
La respuesta del usuario sobre una aplicación ira almacenada en la cola de reglas.
4.1.1.2.2.4 Cola de Reglas Definidas
La repuesta del usuario sobre denegar o permitir a un programa el acceso a la red será
guardada en una lista para ser recordadas.
Cuando el filtro sea llamado por una aplicación comprobará la existencia de esa
aplicación en la lista de reglas. Si no contiene ninguna entrada para esa aplicación,
pasará a crear un nodo en la cola de trabajo para informar al usuario de la nueva
aplicación, por el contrario si existe una regla definida la aplicara.
Recorrer una cola en busca de reglas puede resultar costoso de tiempo, sobre todo para
una aplicación que requiera mucha carga de trabajo con la red, ya que estará pasando
continuamente por el filtro y recorrer la lista cada vez es lento e ineficiente. Por tanto
antes de recorrer la lista de reglas se comprobara la existencia del proceso en una tabla
de hashing.
37
[Firewall: controlando el acceso a la red.]
4.1.1.2.2.5 Tabla Hashing
Una
tabla hash o mapa
hash es
una estructura
de
datos que
asocia llaves o claves con valores. La operación principal que soporta de manera
eficiente es la búsqueda: permite el acceso a los elementos (teléfono y dirección, por
ejemplo) almacenados a partir de una clave generada (usando el nombre o número de
cuenta, por ejemplo). Funciona transformando la clave con una función hash en un hash,
un número que la tabla hash utiliza para localizar el valor deseado.
Figura 3. Tabla hash
En el caso que nos ocupa la llave de la tabla será el PID del proceso. Se creará array
unidimensional que servirá para alojar los PIDs y sus permisos.
Los permisos pueden tomar tres valores:
- El primero de los permisos tomará el valor '?' para designar un Pid que no tenga
ningún tipo de regla definida. En este caso se recorrerá la lista de reglas y podemos
encontrarnos con los siguientes dos supuestos:
-Si existe una entrada para ese programa se añadirá la regla a la tabla de hash
-Si no existe ninguna entrada significará que el programa es nuevo y por tanto deberá
notificarse al usuario que es necesario definir una regla.
Es importante indicar que mientras no se designe una regla la política por defecto será
restrictiva y no se permitirá el acceso a la red al programa.
38
[Firewall: controlando el acceso a la red.]
-Otro valor posible es ‘y’ para cual significa que ese proceso ya ha obtenido el acceso a
la red y se le seguirá permitiendo devolviendo el control al Major original del driver
TCPIP.
- El tercer valor será ‘n’ y significara que ese proceso se le deniega el acceso a la red
por tanto el filtro devolverá un ACCES_DENIED.
El filtro, al ser llamado, comprobará si el Pid del proceso que lo llama está incluido en
la tabla, lo que significará que ese proceso ya ha intentado acceder con anterioridad a la
red y ya se ha definido una regla.
Si no existe en la tabla, obtendrá la ruta y el nombre del programa y comprobara si está
en la lista de reglas. Si esta significará que ese programa ya ha intentado acceder a la red
y se ha definido una regla con anterioridad, pero desde una ejecución anterior del
programa. Añadirá el proceso a la tabla de hash y aplicará la regla definida.
Si no existe en la lista de reglas creara un nuevo nodo a la espera de respuesta por parte
del usuario y denegará el acceso a la red al proceso.
4.1.1.3 Comunicación entre Modo kernel y Modo Usuario
Para que el usuario pueda establecer las políticas de acceso de cada programa a tiempo
real, mientras surgen los programas que desean acceder a la red, debe haber un método
de comunicación entre el driver y el usuario. Para ello se creará un dispositivo accesible
desde los procesos corriendo en modo usuario sobre el cual poder escribir y leer
información para compartir con el driver.
4.1.1.3.1 Creación de Dispositivos
Un driver puede crear diversos dispositivos asociados a él que pueden ser listados y
accesibles por parte de las aplicaciones para interactuar con el driver y los servicios que
ofrezca. Una vez creado el dispositivo, se creará un link para poder ser accedido desde
modo usuario.
39
[Firewall: controlando el acceso a la red.]
Dispositivo = NuevoDispositivo
CrearDispositivo(NuevoDispositivo, nombre, permisos)
CrearLink(nombre, nombreLink)
4.1.1.3.1.1 Estructura de un Dispositivo
La estructura de un dispositivo es estándar y usada por el sistema operativo para
representar el dispositivo, acceder a él y clasificar su tipo, ya que puede ser tanto virtual
como ser un dispositivo físico conectado al sistema.
typedef struct _DEVICE_OBJECT {
CSHORT
Type;
USHORT
Size;
LONG
ReferenceCount;
struct _DRIVER_OBJECT *
DriverObject;
struct _DEVICE_OBJECT *
NextDevice;
struct _DEVICE_OBJECT *
AttachedDevice;
struct _IRP *
CurrentIrp;
PIO_TIMER
Timer;
ULONG
Flags;
ULONG
Characteristics;
__volatile PVPB
Vpb;
PVOID
DeviceExtension;
DEVICE_TYPE
DeviceType;
CCHAR
StackSize;
union {
LIST_ENTRY
ListEntry;
WAIT_CONTEXT_BLOCK Wcb;
} Queue;
ULONG
AlignmentRequirement;
KDEVICE_QUEUE
DeviceQueue;
KDPC
Dpc;
ULONG
ActiveThreadCount;
PSECURITY_DESCRIPTOR
SecurityDescriptor;
KEVENT
DeviceLock;
USHORT
SectorSize;
USHORT
Spare1;
struct _DEVOBJ_EXTENSION * DeviceObjectExtension;
PVOID
Reserved;
} DEVICE_OBJECT, *PDEVICE_OBJECT;
Las partes que interesan para este proyecto son el puntero a IRP, el DriverObject y el
campo Flags.
Como se ha visto anteriormente el IRP nos servirá para la estructura de datos y el paso
de información.
40
[Firewall: controlando el acceso a la red.]
El DriverObject sirve al sistema para saber a qué driver pertenece el dispositivo y quien
tiene el control sobre él.
Mediante el campo Flags se especifica qué tipo de buffer tendrá el dispositivo para la
entrada salida de información.
En este dispositivo se a seteado el campo a Flags al tipo de I/O buffered debido a su
facilidad para acceder desde el driver a la información, aunque por el contrario es una
técnica que usa bastantes recursos.
pDeviceObject->Flags |= DO_BUFFERED_IO;
Con esta disposición de los elementos ya se podrá proceder a la lectura y escritura en el
driver.
4.1.1.3.1.1.1 Lectura
Mediante la lectura del driver se obtendrá la información que quiera pasarnos el driver,
en este caso el programa que intenta acceder a la red y el tipo de acción que pretende
hacer.
Para leer el dispositivo se accederá desde modo usuario como si un fichero se tratase.
hFile = CreateFileA("\\\\.\\Cerbero", GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, 0, NULL);
Cerbero es el nombre del link al dispositivo y hFile es el descriptor que se obtiene para
realizar operaciones sobre él.
ReadFile(hFile, &szTemp, 500, &dwReturn, NULL);
szTemp es el buffer donde se obtiene la información.500 el número de bytes máximos a
leer y dwReturn el número máximo de caracteres a leer.
Mediante una ventana emergente se muestra la información al usuario y se obtiene su
respuesta para ser pasada al driver mediante la escritura en él.
41
[Firewall: controlando el acceso a la red.]
4.1.1.3.1.1.2 Escritura
Una vez se obtiene la respuesta del usuario sobre si permite el acceso o no a la
aplicación a la red se escribirá la respuesta al driver como si se tratase de un archivo con
el descriptor obtenido al abrir el dispositivo.
WriteFile(hFile, respuesta, sizeof(respuesta), &dwReturn, NULL);
Respuesta es si se permite o no el acceso y dwReturn la cantidad de bytes escritos.
La función del driver que recibe la escritura se encargará de obtener la información
pasada por el usuario y añadir la regla a la tabla de hash y a la lista de reglas.
Anexo 4. Función llamada al escribir sobre el dispositivo.
La escritura del dispositivo se produce después de leer la información y esperar la
respuesta del usuario. Pero, ¿cuándo se tiene que leer el dispositivo? ¿Cuándo se sabe
cuándo hay información que leer?
Se aprovechará la característica bloqueante de la función ReadFile para bloquear la
función del driver que se encarga de recibir la lectura hasta que haya información que
notificar mediante sincronismos.
4.1.1.3.2 Sincronismo
El sincronismo se aplicará para informar al major IRP_MJ_READ cuando hay
información que transmitir al usuario. Por lo tanto, la función enlazada a
IRP_MJ_READ quedará bloqueada hasta que reciba la señal de desbloqueo, lo que
significará que hay un nuevo programa sin regla definida, y hay que comunicárselo al
usuario. Para este fin se usarán llamadas a eventos que pueden servir tanto para
sincronizar funciones dentro del mismo driver como para sincronizar el driver con
aplicaciones en modo usuario.
42
[Firewall: controlando el acceso a la red.]
4.1.1.3.2.1 Creación de Eventos
El primer paso será inicializar el evento.
syncEvent = IoCreateNotificationEvent(&eventName, &eventHandle);
Una vez inicializado dispondremos de tres acciones sobre el evento.
KeWaitForSingleObject(syncEventK,Executive,KernelMode, FALSE, NULL);
Con esta instrucción se bloquea la ejecución hasta que se reciba la señal de desbloqueo.
KeSetEvent(syncEvent, 0, FALSE);
Con esta orden se envía la señal de desbloqueo al evento, por lo que el hilo bloqueado
con KeWaitForSingleObject reanudara su ejecución.
KeResetEvent(syncEventK);
Resetea el evento de tal forma que al volver a ser llamada la función
KeWaitForSingleObject el hilo quedara bloqueado, sino se resetea después de un
KeSetEvent la función KeWaitForSingleObject pasara sin ser bloqueante ya que el
evento estará desactivado.
Con estos métodos de sincronización se controla cuando debe ser enviada información
al usuario.
Anexo 5. Función llamada al leer sobre el dispositivo.
43
[Firewall: controlando el acceso a la red.]
4.2
Esquema del Conjunto
Programa Cargador:
Ejecutar (ProgramaControlUsuario)
AbrirManejadorServicios()
CrearServicio(“Cerbero”, Cervero.sys
//Cerbero.sys es el binario del driver
AbrirServicio(“Cerbero”)
IniciarServicio(“cerbero”)
FinalizarCargador()
Programa Control:
evento = InicializarEvento()
Esperar(evento) //Se activa el evento cuando se carga el driver y crea
el dispositivo a leer/escribir
handle = Abrir(“Cerbero”)
Repetir
mensaje = Leer(handle)
PopUp(mensaje,”¿Desea permitir el acceso a la red a este
programa?”, Si/No)
Si ‘si’
Escribir(handle, “permitir”)
Si no
Escribir(handle, “denegar”)
Fin si
Fin Repetir
44
[Firewall: controlando el acceso a la red.]
Driver:
Al cargar el driver:
CrearDispositivo(“Cerbero”)
CrearLink(Cerbero)
evento = InicializarEvento()
Driver->MajorFunction[IRP_MJ_READ] = Leer();
Driver->MajorFunction[IRP_MJ_WRITE] = Escribir();
//Leer y Escribir son funciones propias detalladas posteriormente
DefinirPermiso(“system”,permitir)
DefinirPermiso(“lsass.exe”,permitir)
DefinirPermiso(“svchost.exe”,permitir)
DefinirPermiso(“RunDll32.exe”,permitir)
DesprotegerMemoria()
MajorOriginal = pDrv_tcpip->
MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL]
Substituir(pDrv_tcpip->
MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL],Funcion_filtro())
ProtegerMemoria()
Función Leer:
EsperarEvento(evento)
AbrirMutex()
LeerColaTrabajo()
PrepararBuffer&Pila()
EscribirMensaje(InfoColaTrabajo)
CerrarMutex()
45
[Firewall: controlando el acceso a la red.]
Función Escribir:
PrepararBuffer&Pila()
AbrirMutex()
permiso = LeerPila()
Si permiso = ‘permitir’
NuevaReglaEnLista(InfoColaTrabajo, permitir)
Si no
NuevaReglaEnLista(InfoColaTrabajo, denegar)
Fin si
InfoColaTrabajo = InfoColaTrabajo->siguiente
Si InfoColaTrabajo = vacio
ResetearEvento(evento)
Fin si
CerrarMutex()
Función filtro:
PID = ObtenerPID()
Si TablaPids[PID] = ‘?’
AbrirMutex()
nombre = ObtenerNombrePrograma(PID)
permiso = RecorrerListaReglas(nombre)
Si permiso = permitir
TablaPids[PID] = permitir
Devolver(MajorOriginal)
Si permiso = denegar
TablaPids[PID] = denegar
Devolver(Acces_Denied)
Si permiso = vacío
mensaje = (nombre, ObtenerAccionProceso())
NuevoNodo(ColaTrabajo, mensaje)
TablaPids[PID] = denegar
46
[Firewall: controlando el acceso a la red.]
SetearEvento(evento) //Se informa que hay nuevas
notificaciones
CerrarMutex()
Devolver(Acces_Denied)
Fin Si
Si TablaPids[PID] = permitir
Devolver(MajorOriginal)
Si TablaPids[PID] = denegar
Devolver(Acces_Denied)
Fin Si
47
[Firewall: controlando el acceso a la red.]
Capítulo 5
Evaluación y Juego de Pruebas
48
[Firewall: controlando el acceso a la red.]
En el CD se encuentran los diversos videos sobre las pruebas.
Se puede observar también en los videos la aplicación DbgView mostrando la
información que comunica el driver. De esta manera se sabe que está pasando en el
driver.
Video permitir_msn.avi
Al ejecutar el programa Windows Messenger el firewall nos notifica el intento de la
aplicación a acceder a la red. Mientras no seleccionamos ninguna opción el propio
Messenger nos notifica que no puede conectarse a internet.
Una vez permitimos el acceso a la red si pulsamos el botón de inicio de sesión del
Messenger ya se puede acceder a la cuenta.
Como se observa el firewall ha ejecutado bien el comportamiento deseado.
Video denegar_msn.avi
Al contrario que en la prueba anterior, esta vez se va a denegar el acceso, bloqueando
cualquier conexión.
Se procede como en la prueba anterior pero esta vez en la notificación del firewall se
denegara el acceso a la red. Se puede ver que aunque intentemos conectarnos varias
veces no se puede conectar a internet. Para comprobar el buen funcionamiento de la
lista de reglas se cierra el Messenger y se vuelve a ejecutar. Se puede observar que en la
nueva ejecución no se notifica la conexión al usuario ya que el firewall recorriendo la
lista de reglas ha encontrado que debe restringir su acceso a internet.
49
[Firewall: controlando el acceso a la red.]
Video permitir_navegador.avi
Esta vez el programa objetivo es un navegador web, en concreto Google Chrome.
Como se puede observar en el DbgView el firewall ya ha detectado y definido reglas
antes de ejecutar el navegador.
Al ejecutar el navegador, el firewall detecta el intento de acceso a la red y vuelve a ser
notificado. Tras permitir la conexión ya se puede proceder a un uso normal del
navegador.
Video bloquear_navegador.avi
Se procede como en el caso anterior pero en la notificación se bloque el acceso a la red.
Como se ve aunque se insista o se cierre el programa y se vuelva a abrir se deniega la
conexión a internet.
Video permitir_servidor.avi
En esta demostración se creara un servidor mediante la aplicación NetCat.
Mediante el comando ‘nc –vlp 5050’ el programa se pone a la escucha en el puerto
5050.
Se pueden observar los siguientes sucesos:
-Tras la primera ejecución del programa se puede ver como el programa muestra el
siguiente mensaje: Can’t grap with bind. Esto es debido a la política restrictiva del
firewall.
-Una vez permitido el acceso es necesario reiniciar el programa, esto es usual en las
aplicaciones programadas secuencialmente.
-Tras permitir el acceso las siguientes ejecuciones no son notificadas al usuario ya que
la regla ha sido añadida a la lista de reglas.
50
[Firewall: controlando el acceso a la red.]
Capítulo 6
Recursos Usados
51
[Firewall: controlando el acceso a la red.]
6.1 Bibliografía
Se ofrece a continuación una relación de bibliografía que el autor ha encontrado útil
para trabajar los temas que comprenden este proyecto.
Rootkits: Subverting the Windows Kernel
Autores: Greg Hoglund y Jamie Butler
Páginas: 352
Editorial: Addison-Wesley Professional (Agosto 1, 2005)
Los rootkits son backdoors que dan a un atacante el acceso permanente y prácticamente
indetectable a los sistemas que explota. Ahora, dos de los principales expertos
mundiales, Greg Hoglund y James Butler, han escrito su primera guía completa sobre
rootkits: qué son, cómo funcionan, cómo construirlos y cómo detectarlos. En este libro
revelan lo que nunca antes se dijo sobre los aspectos ofensivos de la tecnología rootkit.
Con su lectura, podrás aprender cómo un atacante puede llegar a entrar y permanecer en
un
sistema
durante
años
sin
ser
detectado.
Hoglund y Butler muestran exactamente cómo destripar los kernels de Windows XP y
Windows 2000, enseñando conceptos que también se pueden aplicar a prácticamente
cualquier sistema operativo moderno, desde Windows Server 2003 hasta Linux y Unix.
Usando una gran cantidad de ejemplos, se enseñan técnicas de programación de rootkits
que pueden ser utilizados por un amplia gama de software, desde herramientas de
seguridad hasta drivers y depuradores.
The Windows 2000 Device Driver Book: A Guide for Programmers
Autores: Art Baker y Jerry Lozano
Páginas: 480
Editorial: Prentice Hall; 2 edición (Noviembre 30, 2000)
52
[Firewall: controlando el acceso a la red.]
6.2 Páginas Web
www.rootkit.com
Actualmente la web esta fuera de servicio, pero contenía información y artículos
publicados por programadores de rootkits y técnicas que requieren acceso al kernel de
Windows.
www.elhacker.net
Pagina española dedicada a la seguridad web y programación en general.
http://msdn.microsoft.com
MSDN es
una fuente de información esencial para desarrolladores que utilizan
herramientas, productos, tecnologías y servicios de Microsoft. MSDN Library incluye
procedimientos y documentación de referencia, código de ejemplo, artículos técnicos,
entre otros recursos.
6.3 Software
Se ofrece a continuación una relación de programas que el autor ha necesitado para
llevar a cabo la implementación del proyecto.
6.3.1 Visual Studio 2010
Microsoft Visual
Studio es
un entorno
de
desarrollo
integrado para
sistemas
operativos Windows. Soporta varios lenguajes de programación tales como Visual
C++, Visual C#, Visual J#, ASP.NET y Visual Basic .NET, aunque actualmente se han
desarrollado las extensiones necesarias para muchos otros.
Visual Studio permite a los desarrolladores crear aplicaciones, sitios y aplicaciones web,
así como servicios web en cualquier entorno que soporte la plataforma .NET (a partir de
la versión net 2002). Así se pueden crear aplicaciones que se intercomuniquen entre
estaciones de trabajo, páginas web y dispositivos móviles.
Para el proyecto que nos ocupa se ha usado para la programación en C del programa de
control del firewall.
53
[Firewall: controlando el acceso a la red.]
6.3.2 Windows Drivers Kit
Windows Driver Kit (WDK) versión 7.0.0 contiene las herramientas, ejemplos de
código, documentación, compiladores, los encabezados y bibliotecas con las que los
desarrolladores de software pueden crear controladores para Windows 7, Windows
Vista, Windows XP, Windows Server 2008 R2, Windows Server 2008, y Windows
Server 2003.
Se ha usado para compilar el driver del firewall para la plataforma Windows 7 en su
versión de 32 bits.
6.3.3 WinDbg
Windbg es un completo depurador de Microsoft, tanto en modo usuario como en modo
núcleo. El paquete Debugging Tools for Windows incorpora un buen número de
depuradores y utilidades relacionadas.
Se ha usado durante el proyecto para depurar y analizar pantallazos azules durante la
ejecución del driver.
6.3.4 DbgView
DebugView es una utilidad que deja supervisar y eliminar errores de salida en un
sistema local, es capaz de mostrar la salida debug (el depurador) tanto en modo kernel
(núcleo del sistema) como en modo usuario.
Se ha usado para mostrar información del driver a través del debug para analizar su
funcionamiento y valores obtenidos.
54
[Firewall: controlando el acceso a la red.]
7. Conclusiones
En este PFC se ha analizado, diseñado y desarrollado un sistema de control de las
aplicaciones que pueden acceder a la red. Las políticas de acceso las pone el usuario de
manera interactiva cuando un nuevo programa intenta acceder a la red.
Para tal fin, se ha tenido que dividir el proyecto en tres partes (cargador, driver y
programa de control) con una implementación que suman alrededor de mil líneas de
código.
Cabe destacar que para la programación de este proyecto se ha tenido que conocer y
estudiar a fondo las estructuras del núcleo de Windows y la API específica para
programar drivers.
La dificultad de programar drivers radica principalmente en unas estructuras complejas
y en que el mínimo error puede provocar un reinicio del sistema (pantalla azul de
Windows) o incluso corromper partes del sistema.
7.1 Comparación con Otros Productos
Comparando el firewall que viene con cualquier Windows 7 de serie, se puede observar
como el del proyecto evita fugas de información, mientras no es escogida una regla por
el usuario se le deniega el acceso a la red. En cambio, en el de Windows se permite el
acceso hasta que el usuario no lo deniega explícitamente.
Imaginemos el siguiente suceso. Se introduce un pendrive, infectado con un troyano o
un malware que obtenga las contraseñas almacenadas y las envié por correo, en un
ordenador. El usuario se aleja del pc o está pendiente de otras cosas (televisión, revistas,
hablando…) y el autorun ejecuta el malware. El usuario no vera la alerta del firewall de
Windows y mientras no cancele los permisos, el malware podrá enviar los datos
recolectados sin impedimento alguno. Ver video ‘comparación’ en el CD.
55
[Firewall: controlando el acceso a la red.]
Comparado con productos de pago como Kaspersky o Zone Alarm, por nombrar
algunos, queda manifiesta la superioridad de estos debido a sus capacidades para definir
más reglas (aceptar conexiones entrantes pero no salientes o viceversa…).El otro punto
que distancia la calidad de los productos nombrados con el proyecto es la inexistencia
de necesitar reiniciar el programa para aplicar las políticas de seguridad. En el caso del
proyecto determinados programas deben ser reiniciados para que se aplique la regla de
poder acceder a la red. Aparte de su superioridad en entorno gráfico y poder visualizar y
modificar reglas definidas o añadirlas visualmente.
7.2 Posibles Mejoras
Se podría implementar un entorno grafico que quedara minimizado en el system tray
(iconos del lado del reloj de Windows) y con él poder listar, modificar y agregar reglas.
También se podría implementar una pequeña base de datos con las reglas y cargarlas al
inicio del driver para ser recordadas en posteriores cargas del firewall. Una pequeña
base de datos con una pequeña estructura que entendiese el driver sería suficiente. El
fichero contenedor de la base de datos se mantendría cifrado en disco para evitar que
posibles programas malintencionados se agregasen a sí mismos a las excepciones del
firewall.
7.3 Como Proseguiría con el Proyecto
Si se contemplase la posibilidad de proseguir con el proyecto los principales objetivos a
conseguir serian inviolabilidad del firewall y mayor información.
La inviolabilidad haría referencia a evitar que programes malintencionados alterasen el
funcionamiento normal del firewall. Unos ejemplos de ello serian por ejemplo hookear
la API ZwOpenProcess para evitar que modificasen o cerrasen el programa de control.
También comprobar cada cierto tiempo que el major substituido del driver TCPIP sigue
apuntando a la función filtro del driver del firewall. Podría darse el caso que un malware
56
[Firewall: controlando el acceso a la red.]
siguiendo las técnicas expuestas en este proyecto quitara el hook de el firewall y dejara
a la aplicación ciega ante conexiones de red.
La información que se podría recolectar seria tratando la estructura Irp de las llamas al
filtro para obtener información como a que dirección intenta conectarse el programa, el
puerto destino, el puerto origen…
Teniendo información como la anterior se podrían definir ciertas reglas según el destino
de la conexión o a los servicios que va destinado (por ejemplo denegándolo todo menos
el puerto 80 solo se permite acceso a servidores web).
57
[Firewall: controlando el acceso a la red.]
Capítulo 8
Planificación Temporal
58
[Firewall: controlando el acceso a la red.]
59
[Firewall: controlando el acceso a la red.]
El tiempo dedicado ha resultado una media de cuatro horas a la semana durante los ocho
meses dedicados al proyecto.
60
[Firewall: controlando el acceso a la red.]
Capítulo 9
Manual de Uso
61
[Firewall: controlando el acceso a la red.]
Dentro del CD se encuentra diverso material como la documentación, binarios,
proyectos, planificación temporal y videos de prueba.
Los binarios incluidos están compilados para una plataforma Windows 7 de 32 bits.
Se puede lanzar el firewall pulsando el botón derecho del ratón sobre el archivo
‘loader.exe’ y pulsando ‘Ejecutar como administrador’. A partir de entonces cuando un
programa intente comunicarse con la red aparecerá lo siguiente en pantalla:
Si se pulsa ‘no’ no se permitira el acceso a la red por parte del programa mostrado, por
el contrario si se pulsa ‘si’ se permitira su acceso. Dependiendo de cómo este
implementado sera necesario reiniciar el programa en cuestion.
También se incluye el programa DbgView que ejecutándolo en modo administrador nos
permitirá ver los mensajes del driver hacia el debug. Eso nos permitirá saber que esta
ocurriendo en cada momento. Ver video Cargar_firewall.avi.
Dependiendo
de la
plataforma debería ser recompilado
para
un
correcto
funcionamiento, se ha de recordar que un mal funcionamiento significaría una panta
azul y reinicio del sistema y una posible corrupción del sistema.
Para compilar el driver desde la consola de Windows Drivers Kits se accede al
directorio de las fuentes mediante el comando ‘cd ruta’ y se compila con ‘build –cZ’.
Los otros dos programas se pueden compilar mediante el IDE de Visual Studio 2010.
62
[Firewall: controlando el acceso a la red.]
Capítulo 10
Anexo: Partes del Código Fuente
63
[Firewall: controlando el acceso a la red.]
Anexo 1.
__asm
{
push eax
//se guarda el valor de EAX
mov
//se mueve el registro a EAX
eax, CR0
and eax, 0FFFEFFFFh
de escritura
//se desactiva el bit de protección
mov
CR0, eax
//se escribe el registro
pop
eax
//se restablece el valor de EAX
}
//Modificaciones de memoria a hacer.
__asm
{
push eax
//se guarda el valor de EAX
mov
eax, CR0
//se mueve el registro a EAX
eax, 0FFFEFFFFh
//se activa el bit de protección de
mov
CR0, eax
//se escribe el registro
pop
eax
//se restablece el valor de EAX
and
escritura
}
64
[Firewall: controlando el acceso a la red.]
Anexo 2
NTSTATUS InstalarTCPDriverHook(){
NTSTATUS
ntStatus;
//Estado de las operaciones
UNICODE_STRING deviceTCP;
//Nombre del dispositivo TCP en unicode
WCHAR deviceTCPNombre[] = L"\\Device\\Tcp";
//Nombre del dispositivo TCP
pFile_tcp = NULL;
//Inicializacion del objeto TCP
pDev_tcp
= NULL;
//Inicializacion del dispositivo TCP
pDrv_tcpip = NULL;
//Inicializacion del driver TCP
RtlInitUnicodeString (&deviceTCP, deviceTCPNombre);
//Paso de la cadena a unicode
ntStatus = IoGetDeviceObjectPointer(&deviceTCP, FILE_READ_DATA,
&pFile_tcp, &pDev_tcp);
//Obtengo el puntero al inicio del
dispositivo TCP
if(!NT_SUCCESS(ntStatus))
mal se sale
//Si algo a ido
return ntStatus;
pDrv_tcpip = pDev_tcp->DriverObject;
puntero al driver TCP
//Obtengo el
OldIrpMjDeviceControl = pDrv_tcpip>MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL];
//Guardo la direccion del IRP_MJ_INTERNAL_DEVICE_CONTROL del
driver
__asm
65
[Firewall: controlando el acceso a la red.]
{
push eax
//se guarda el valor de EAX
mov
//se mueve el registro a EAX
and
escritura
eax, CR0
eax, 0FFFEFFFFh //se desactiba el bit de protección de
mov
CR0, eax
//se escribe el registro
pop
eax
//se restablece el valor de EAX
}
if (OldIrpMjDeviceControl){
//Si se obtiene bien
InterlockedExchange ((PLONG)&pDrv_tcpip>MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL],
(LONG)HookedDeviceControl);
//Substituyo la direccion de
memoria de la funcion IRP_MJ_INTERNAL_DEVICE_CONTROL del driver por mi
propia funcion.
}
__asm
{
push eax
//se guarda el valor de EAX
mov
//se mueve el registro a EAX
and
escritura
eax, CR0
eax, 0FFFEFFFFh //se actiba el bit de protección de
mov
CR0, eax
//se escribe el registro
pop
eax
//se restablece el valor de EAX
}
return STATUS_SUCCESS;
}
66
[Firewall: controlando el acceso a la red.]
Anexo 3
nuevo = (PCOLA_TRABAJO)ExAllocatePoolWithTag(NonPagedPool,
sizeof(COLA_TRABAJO), ' pxE');
Anexo 4
NTSTATUS Escribir(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PIO_STACK_LOCATION pIoStackIrp = NULL;
//Puntero a la pila actual
PCHAR entrada;
//Puntero a la cadena donde se alojara el contenido
escrito
PCOLA_PATH nuevo;
__try{
nuevo = (PCOLA_PATH)ExAllocatePoolWithTag(NonPagedPool,
sizeof(COLA_PATH), ' pxE');
KeWaitForMutexObject(&mutex, Executive, KernelMode, FALSE,
NULL);
pIoStackIrp = IoGetCurrentIrpStackLocation(Irp);
obtiene el puntero a la actual pila
//Se
if(pIoStackIrp)
//Si se a obtenido el puntero a la pila
correctamente...
{
entrada = (PCHAR)Irp->AssociatedIrp.SystemBuffer;
asocia un buffer con la pila
if(entrada)
//Si se ha asociado correctamente...
{
67
//Se
[Firewall: controlando el acceso a la red.]
if(StringTerminada(entrada, pIoStackIrp>Parameters.Write.Length))
//Se comprueba si tiene caracter
termiandor
{
nuevo->siguiente = NULL;
if(entrada[0] == 'y'){
PIDS[(int)primer_trb->pid] = 'y';
nuevo->permitido = 1;
RtlStringCbPrintfA(nuevo->path, 500 *
sizeof(char),"%s",primer_trb->path);
nuevo->siguiente = NULL;
DbgPrint("[CERBERO] Se permite:
%s\n",nuevo->path);
}
else{
PIDS[(int)primer_trb->pid] = 'n';
nuevo->permitido = 0;
RtlStringCbPrintfA(nuevo->path, 500 *
sizeof(char),"%s",primer_trb->path);
nuevo->siguiente = NULL;
DbgPrint("[CERBERO] NO Se permite:
%s\n",nuevo->path);
}
ultimo->siguiente = nuevo;
ultimo = nuevo;
if(primer_trb->siguiente)
primer_trb = primer_trb->siguiente;
else{
primer_trb = NULL;
ultimo_trj = NULL;
}
if(primer_trb == NULL)
KeResetEvent(syncEventK);
68
[Firewall: controlando el acceso a la red.]
}
}
}
KeReleaseMutex(&mutex,FALSE);
}__except(EXCEPTION_EXECUTE_HANDLER){
NtStatus = GetExceptionCode();
DbgPrint("[CERBERO] Exception escribir: %d.\n", NtStatus);
}
return NtStatus;
}
Anexo 5
NTSTATUS Leer(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
NTSTATUS NtStatus = STATUS_BUFFER_TOO_SMALL;
memoria disponible por defecto
//No hay
PIO_STACK_LOCATION pIoStackIrp = NULL;
//Puntero a la pila actual
UINT longitud;
//Longitud del mensaje a enviar
UINT longitudLeida = 0;
//Longitud del total de bytes anviados
PCHAR bufferLectura;
//Puntero a la cadena compartida con el usuario
__try{
KeWaitForSingleObject(syncEventK,Executive,KernelMode, FALSE,
NULL);
KeWaitForMutexObject(&mutex, Executive, KernelMode, FALSE,
NULL);
RtlStringCbLengthA(primer_trb->mensaje, 500* sizeof(char),
&longitud);
//Obtengo la longitud de la cadena mensaje
69
[Firewall: controlando el acceso a la red.]
pIoStackIrp = IoGetCurrentIrpStackLocation(Irp); //Puntero a la
actual pila
if(pIoStackIrp)
//Si se a obtenido el puntero a la pila correctamente...
{
bufferLectura = (PCHAR)Irp->AssociatedIrp.SystemBuffer;
//Se asocia un buffer con la pila
if(bufferLectura && pIoStackIrp->Parameters.Read.Length >=
longitud)
//Si se a asociado correctamente y se dispone de espacio
para copiar el mensaje...
{
longitud);
RtlCopyMemory(bufferLectura, primer_trb->mensaje,
//Se copia el mensaje al buffer de lectura
longitudLeida = longitud;
//Bytes a enviar
NtStatus = STATUS_SUCCESS;
//Todo correcto
}
}
Irp->IoStatus.Status = NtStatus;
//Todo correcto
Irp->IoStatus.Information = longitudLeida;
bytes a leer
//Numero de
IoCompleteRequest(Irp, IO_NO_INCREMENT);
completada
//Rutina
KeReleaseMutex(&mutex,FALSE);
}__except(EXCEPTION_EXECUTE_HANDLER){
NtStatus = GetExceptionCode();
DbgPrint("[CERBERO] Exception leer: %d.\n", NtStatus);
}
return NtStatus;
//Devuelve el estado
}
70
[Firewall: controlando el acceso a la red.]
71
Descargar