Mi computadora no se puede encender. archivo doc en microsoft word? que hacemos
Snort, como sistema liviano de detección de intrusiones en la red, puede tener algunas deficiencias en las aplicaciones prácticas, pero si desea comprender el principio de funcionamiento de IDS, estudie cuidadosamente su fuente. El código es muy bueno.
En primer lugar, hagamos un repaso general del snort.
En términos de principio de funcionamiento, Snort es un NIDS. [Nota: Los sistemas de detección de intrusos basados en la red (NIDS) inspeccionan pasivamente los datos de transmisión de la red sin procesar en un determinado punto de la red. Al analizar los paquetes de datos inspeccionados, NIDS puede comparar las características del comportamiento de intrusión o detectar comportamientos anormales desde la perspectiva de la actividad de la red. ] Para recopilar datos de transmisión de red, utilice el paquete de herramientas libpcap. Snort analiza los datos recopilados por libpcap para determinar si existe actividad sospechosa en la red.
En términos de métodos de detección, el snort es básicamente una detección de uso indebido. [Nota: este método coincide con los patrones característicos de ataques conocidos, incluido el uso de rastreadores que funcionan en modo promiscuo de la tarjeta de red para el análisis de protocolo pasivo y la interpretación y análisis de las características de una serie de paquetes de datos. Por cierto, otro tipo de detección es la detección de anomalías. En términos de implementación específica, es solo la búsqueda y comparación de datos más directa y simple, y no implica métodos de detección de intrusiones más complejos.
Aunque snort no cuenta con estrategias de detección avanzadas en su implementación, nos proporciona un ejemplo muy
excelente de un sistema de detección de intrusos de código abierto. Podemos analizar el código de IDS para comprender cómo funciona y agregar nuestras propias ideas en base a esto.
El estilo de programación de Snort es excelente, el código no es difícil de leer y toda la estructura del programa es clara.
La relación aullante no es complicada. Pero Snort tiene muchos archivos fuente y muchas funciones, por lo que no es muy fácil de explicar. Por lo tanto, es mejor repasar el código una o dos veces para que quede más claro.
********************************************* *** **********
****************************** **** ***********************
Echemos un vistazo a la estructura general de Snort. Expanda el paquete comprimido de snort, que contiene alrededor de 50 programas C y archivos de encabezado, y alrededor de otros 30 archivos (archivos de proyecto, datos o descripción). [Nota: aquí se utiliza snort-1.6-beta7. No tengo snort-1.6.3 a mano, así que estoy usando la versión anterior y no hay mucha diferencia. ] Los archivos de código fuente se agrupan de la siguiente manera.
Snort.c (.h) es el archivo donde se encuentra el programa principal, el cual implementa la función principal y una serie de funciones auxiliares.
Decode.c(.h) elimina el paquete capa por capa para determinar a qué protocolo pertenece el paquete y cuáles son sus características. y
en la variable de estructura global pv.
Log.c(.h) implementa funciones de grabación y alarma. Snort tiene múltiples formatos de registro, uno se almacena en formato binario tcpdump y el otro se almacena en el directorio de registro en formato ascii codificado por snort. El nombre del directorio de registro se denomina según la dirección IP del host "extranjero". Las alertas tienen diferentes niveles y métodos y pueden registrarse en el registro del sistema o en archivos especificados por el usuario. Además, los mensajes de alarma se pueden enviar a través de sockets Unix y los mensajes winpopup se pueden enviar a sistemas Windows a través de SMB.
Mstring.c(.h) implementa el algoritmo de coincidencia de cadenas. En Snort se utiliza el algoritmo de Boyer-Moore. Los algoritmos generalmente se encuentran en libros.
Plugbase.c(.h) implementa un conjunto de funciones para inicializar la detección y registrar reglas de detección. Las reglas de detección en Snort se almacenan en forma de una lista vinculada y cada regla se agrega a la lista vinculada mediante el proceso de registro.
Response.c(.h) responde, es decir, envía activamente paquetes de datos al atacante. Aquí se implementan dos respuestas. Una es enviar información ICMP falsa a la que el host no puede acceder y la otra es enviar paquetes RST para desconectar el TCP.
Rule.c(.h) implementa las funciones necesarias para la configuración de reglas y la detección de intrusiones. La función principal de la configuración de reglas es convertir el archivo de reglas en una lista de reglas en la operación real. La función de detección detecta firmas de ataques según reglas.
Sp_*_check.c(.h) es la implementación específica de diferentes tipos de reglas de detección. Es fácil conocer las reglas implementadas por el nombre del archivo. Por ejemplo, sp sp_dsize_check se refiere al tamaño de datos del paquete de datos, sp_icmp_type_check se refiere al tipo de paquete de datos icmp y sp_tcp_flag_check se refiere al bit de bandera del paquete de datos tcp. Sin más detalles.
Spo_*. c(.h) implementa reglas de salida. Spo_alert_syslog registra eventos en syslog; Spo_log_tcpdump usa la función de registro en libpcap para registrar registros.
Spp_*. c(.h) implementa reglas de preprocesamiento. Incluyendo la definición de t, la función sale. ProcessPacket es el programa clave aquí.
Finalmente, finaliza el proceso de adquisición.
********************************************* *** **********
Ahora veamos cómo snort analiza paquetes y detecta intrusiones.
En la última parte de la función principal, hay las siguientes declaraciones más importantes:
/*Leer todos los paquetes de datos en el dispositivo. Continúe hasta que se lea el paquete cnt */
if(pcap_loop(pd, pv.pkt_cnt, (pcap_handler)ProcessPacket, NULL)<0)
{ p>
......
}
Aquí, la función pcap_loop tiene cuatro parámetros, que se explican respectivamente:
Pd es una variable global, indicando el descriptor de archivo, que ha sido asignado correctamente en la llamada anterior de OpenPcap. Como se mencionó anteriormente, snort puede recopilar datos de la red en tiempo real o leer datos de archivos para analizarlos. Al abrir un archivo (o dispositivo) en diferentes circunstancias, pd se utiliza para manejar la interfaz del archivo o dispositivo de tarjeta de red, respectivamente.
Pd es un puntero de tipo struct pcap, que incluye el descriptor del archivo real, el búfer y otros campos utilizados para procesar la obtención de información del archivo correspondiente.
La instrucción para asignar un valor a pd en la función OpenPcap es la siguiente:
/*Obtenga el descriptor de archivo del dispositivo y abra la interfaz de red*/
pd = pcap_open_live(pv .interface, snaplen,
pv.promisc_flag? PROMISC: 0, READ_TIMEOUT, error buf
o
/*Open); file, open file*/
pd = pcap_open_offline(intf, error buf
Por lo tanto, este parámetro indica de dónde obtener los datos a analizar.
El segundo parámetro es pv.pkt_cnt, que representa el número total de paquetes a capturar. Cuando se inicializa la función principal, la configuración predeterminada es -1, que se convierte en un bucle eterno, que se captura hasta que el programa sale:
/*Inicializa el contador de paquetes para que se repita para siempre*/
PV . PKT _ CNT =-1;
O establezca el número de paquetes a capturar en la línea de comando. En la llamada anterior a la función ParseCmdLine, se encontró el parámetro n y se restableció el valor de pv.pkt_cnt. Las declaraciones relevantes en ParseCmdLine son las siguientes:
Caso "n":/*Obtener x paquetes de datos y salir*/
PKT _ CNT = atoi(optarg);
p>El tercer parámetro es la función de devolución de llamada, que maneja los paquetes capturados.
Esta es la función
ProcessPacket, que se explica en detalle a continuación.
El cuarto parámetro es un puntero de cadena que indica el usuario, que aquí se establece en nulo.
Antes de explicar las funciones de ProcessPacket, es necesario explicar la implementación de pcap_loop. Vemos que la función principal solo llama a pacp_loop una vez en el juicio condicional if, por lo que el bucle debe realizarse en pcap_loop. Al observar la parte de implementación de pcap_loop en el archivo pcap.c, encontramos que es:
(Igual que las organizaciones internacionales) Organizaciones internacionales
pcap_loop(pcap_t *p, int cnt , devolución de llamada pcap_handler, u_char * usuario)
{
Registrar int n;
for(;{//for loop
if (p->;sf .rfile! = vacío)
n = pcap_offline_read(p, cnt, callback, user);
En caso contrario{
/*
*XXX continuar leyendo hasta que obtengamos algo
*(o se produzca un error)
*/
Hacer {//do bucle
n = pcap_read(p, cnt, devolución de llamada, usuario
} while (n = = 0); p>If (n & lt= 0)
Return(n); //Encuentra un error y regresa
if(CNT & gt; 0) {
CNT- = n;
if(CNT & lt; = 0)
return(0); //Cuando se alcanza el número especificado, regresa
}
// Solo las dos situaciones de retorno anteriores
}
}
Ahora observe la implementación de ProcessPacket , que se utiliza para procesar datos. La función de devolución de llamada del paquete. El tipo de esta función es pcap_handler. La definición de tipo en pcap.h es la siguiente:
typedef void(* pcap _ handler)( u _ char *, const struct pcap_pkthdr *,
const u _ char *);
El parámetro 1 es inútil aquí;
El segundo parámetro es pcap_pkthdr puntero de estructura, que registra la marca de tiempo, la longitud del paquete y la longitud de captura;
El tercer puntero de cadena de parámetro es un paquete.
La función es la siguiente:
<. p>void ProcessPacket(char *user, struct pcap_pkthdr * pkthdr, u_char *pkt){
Paquete de datos p //decodificar. para registrar diversa información del paquete
/* Llame al decodificador de paquetes, llame a la función de desempaquetar, donde el molinillo es global.
El puntero de función se ha configurado en la función de desempaquetado correcta */
(*Grinder)(&p,pkthdr,PKT);
/*Will The El paquete se imprime en la pantalla, si se selecciona el modo de visualización detallada,
luego muestra los datos del paquete en la salida estándar*/
if(pv.verbose_flag)
{
......//Omitir
}
/*Verificar o registrar paquetes de datos según sea necesario
Si el trabajo utiliza reglas de detección, llame al preprocesamiento para la detección.
De lo contrario, simplemente registre la información del paquete*/
If (!pv.use_rules)
{
.../ / Registro, omitido
}
Otros
{
Preprocesamiento. p);
}
//Borrar el búfer
ClearDumpBuf();
}
Aquí , la función de preprocesamiento realiza la detección real.
********************************************* *** **********************************
La función Proprocess es muy corta. Primero, se llaman las reglas de preprocesamiento para procesar el paquete de datos P y luego se llama a la detección.
La función Detect detecta la implementación de la coincidencia de reglas. Si se logra la coincidencia de reglas, se llama a la función CallOutput.
Insertar alertas o registros basados en reglas de salida. La función es la siguiente:
Preprocesamiento no válido(paquete *p)
{
PreprocessFuncNode * idx
do_detect = 1;
idx = PreprocessList //Apunta al encabezado de la lista de reglas de preprocesamiento
Y (idx! = NULL) //Llama a la función de preprocesamiento para procesar el paquete p.
{
idx-& gt; func(p);
idx = idx-& gt; /p>
If(!p->;frag_flag&&do_detect)
{
If(Detect(p)) //Llama a la función de detección.
{
CallOutputPlugins(p); //Si coincide, genera según las reglas.
}
}
}
Aunque esta función es muy concisa, en la línea 1 vemos que ProprocessFuncNode está definido.
El puntero apunta al tipo de estructura, por lo que a continuación comenzaremos a cubrir las diversas complejidades de Snort.
La estructura de datos. En el análisis anterior, siempre seguí el orden de llamada del programa e ignoré muchas letras.
Números (de hecho hay muchos muy importantes) para describir la línea principal de ejecución de snort para evitar confusiones causadas por una gran cantidad de relaciones de llamadas en el programa
. Hasta ahora, no hemos tocado las estructuras de datos centrales de Snort.
Y algoritmos. Hay una serie de preguntas clave que deben abordarse: ¿Cómo se describen estáticamente las reglas? En tiempo de ejecución, ¿bajo qué estructura se almacenan dinámicamente estas reglas? ¿Cómo se llaman los controladores de cada regla? Snort dio
Ofrecemos un buen método.
Una idea muy exitosa de snort es utilizar el mecanismo de complemento. La función de procesamiento de reglas no está fijada en el
programa fuente, sino que se lee desde el archivo de reglas. de acuerdo con la configuración de los parámetros de cada regla de ejecución, y luego las funciones de procesamiento requeridas por cada regla se vinculan a una lista vinculada. Durante la detección real, estas listas vinculadas se atraviesan y se llama a las listas vinculadas.
La función correspondiente a analizar.
La estructura de datos principal de Snort es una lista enlazada, casi todas listas enlazadas a listas enlazadas. Seamos una introducción general
.
Necesitamos regresar y observar la inicialización de algunas reglas en la función principal.
Estructura de datos.
Cuando la función principal inicializa las reglas, primero establece varias listas enlazadas y las variables globales se definen de la siguiente manera.
(en plugbase.c):
KeywordXlateList * KeywordList
lista de palabras clave de preproceso * palabras clave de preproceso;
PreprocessFuncNode * PreprocessList <; /p>
lista de palabras clave de salida * palabras clave de salida;
OutputFuncNode * OutputList
Se omiten las definiciones específicas de estas estructuras. Este proceso de inicialización pone en funcionamiento las claves predefinidas.
Las palabras y las funciones de procesamiento están conectadas en diferentes listas vinculadas según las categorías. Luego, al analizar el archivo de reglas,
si las opciones de la regla contienen palabras clave, la lista vinculada correspondiente se inicializará desde arriba.
Busque y agregue la información necesaria y la funcionalidad de procesamiento al nodo que representa esta regla (usando el tipo RuleTreeNode
, que se describe en detalle a continuación) en un dominio específico (el tipo OptTreeNode). .
Al mismo tiempo, al final de las reglas de inicialización en la función principal, se analiza el archivo de reglas especificado. En la mayoría de los casos
En resumen, hay tres variables globales que contienen las reglas (rules.c):
Alerta de encabezado de lista /*título del bloque de alerta*/
Registro de encabezado de lista;/*Encabezado de bloque de registro*/
Pasar Listerhead;/*Pasar encabezado de bloque*/
Estas variables son de tipo ListHead y, como sugiere el nombre, , representa el encabezado de una lista vinculada. Regístrese en alerta
Para las reglas que deben recibir alertas, las reglas que deben registrarse se registran en el registro y las reglas que se registran en el pase están en.
Ignora el proceso (no se realiza ningún procesamiento). El encabezado de la lista se define de la siguiente manera:
typedef struct _ListHead
{
RuleTreeNode * TcpList
RuleTreeNode * UdpList
RuleTreeNode * IcmpList
} ListHead
Como puede ver, hay tres punteros en cada estructura ListHead, que apuntan al encabezado de la lista vinculada para procesar Tcp/Udp/ Reglas de paquetes icmp. Aquí aparece una nueva estructura RuleTreeNode. Para ilustrar la relación jerárquica de la lista vinculada, la definición de RuleTreeNode se enumera a continuación, pero la mayoría de los campos se ignoran:
typedef struct _RuleTreeNode
{
RuleFpList * regla _ func
......//Ignorar
estructura _ RuleTreeNode * derecha
OptTreeNode * abajo/*Opciones de regla asociadas con esta Lista
Rule Node*/
} RuleTreeNode
RuleTreeNode contiene los tres campos de puntero anteriores, que pueden formar tres listas vinculadas respectivamente. El lado derecho del tipo RuleTreeNode* apunta al siguiente RuleTreeNode, que es equivalente al siguiente campo en una lista enlazada ordinaria, pero se nombra aquí. Esto forma una lista enlazada regular.
Rule_func, un puntero a la clase RuleFpList, registra la lista vinculada de controladores de reglas. A veces es necesario analizar una regla llamando a varios controladores. Por eso es necesario hacer una lista enlazada. Echemos un vistazo a las definiciones a continuación.
Además del siguiente campo, también hay un puntero de función:
typedef struct _RuleFpList
{
/*Puntero de función de verificación de reglas*/
int (*RuleHeadFunc)(Packet *, struct _RuleTreeNode *, struct _ RuleFpList *);
/*Puntero al siguiente nodo de función de regla*/
struct _ RuleFpList * next
} RuleFpList
El tercer campo de puntero es el puntero hacia abajo de la clase OptTreeNode. El comentario después de esta línea indica claramente que se trata de una opción de regla asociada con este nodo de regla. Lista enlazada. Desafortunadamente, la estructura de OptTreeNode también es bastante compleja y se introducen varias listas enlazadas nuevas. Ignorando algunos campos, OptTreeNode se define de la siguiente manera:
typedef struct _ OptTreeNode
{
/*Plug-in/función de detección aquí*/
OptFpList * opt_func
/* ds_list es absolutamente necesario para que el sistema de complementos funcione,
permite a los autores de complementos asociar estructuras de datos "dinámicas"
Tener un sistema de reglas les permite vincular cualquier cosa que se les ocurra
Con lista de reglas*/
void * ds_list[512] /* Lista de punteros de estructura de datos de complementos*/
..........//Algunos campos están omitidos.
struct _ OptTreeNode * next
} OptTreeNode
Next apunta al siguiente nodo en la lista vinculada, no hace falta decirlo. El puntero opt_func del tipo OptFpList apunta a la lista vinculada de la función de opción
, que no es muy diferente de RuleFpList mencionada anteriormente. Vale la pena señalar la variedad de sugerencias.
Ds_list se utiliza para registrar el procesamiento predefinido involucrado en la regla. El tipo de cada elemento es void*. Cuando realmente representa las reglas, ds_list se convierte a diferentes tipos predefinidos.
-
La función Proprocess es muy corta. Primero, se llaman las reglas de preprocesamiento para procesar el paquete de datos P y luego se llama a la detección.
La función Detect detecta la implementación de la coincidencia de reglas. Si se logra la coincidencia de reglas, se llama a la función CallOutput.
Insertar alertas o registros basados en reglas de salida. La función es la siguiente:
Preprocesamiento no válido(paquete *p)
{
PreprocessFuncNode * idx
do_detect = 1;
idx = PreprocessList //Apunta al encabezado de la lista de reglas de preprocesamiento
Y (idx! = NULL) //Llama a la función de preprocesamiento para procesar el paquete p.
{
idx-& gt; func(p);
idx = idx-& gt; /p>
If(!p->;frag_flag&&do_detect)
{
If(Detect(p)) //Llama a la función de detección.
{
CallOutputPlugins(p); //Si coincide, genera la salida de acuerdo con las reglas.
}
}
}
Aunque esta función es muy concisa, en la línea 1 vemos que ProprocessFuncNode está definido.
El puntero apunta al tipo de estructura, por lo que a continuación comenzaremos a cubrir las diversas complejidades de Snort.
La estructura de datos. En el análisis anterior, siempre seguí el orden de llamada del programa e ignoré muchas letras.
Números (de hecho hay muchos muy importantes) para describir la línea principal de ejecución de snort para evitar confusiones causadas por una gran cantidad de relaciones de llamadas en el programa
. Hasta ahora, no hemos tocado las estructuras de datos centrales de Snort.
Y algoritmos. Hay una serie de preguntas clave que deben abordarse: ¿Cómo se describen estáticamente las reglas? En tiempo de ejecución, ¿bajo qué estructura se almacenan dinámicamente estas reglas? ¿Cómo se llaman los controladores de cada regla? Snort dio
Ofrecemos un buen método.
Una idea muy exitosa de snort es utilizar el mecanismo de complemento. La función de procesamiento de reglas no está fijada en el
programa fuente, sino que se lee desde el archivo de reglas. de acuerdo con la configuración de los parámetros de cada regla de ejecución, y luego las funciones de procesamiento requeridas por cada regla se vinculan a una lista vinculada. Durante la detección real, estas listas vinculadas se atraviesan y se llama a las listas vinculadas.
La función correspondiente a analizar.
La estructura de datos principal de Snort es una lista enlazada, casi todas listas enlazadas a listas enlazadas. Seamos una introducción general
.
Necesitamos regresar y observar la inicialización de algunas reglas en la función principal.
Estructura de datos.
Cuando la función principal inicializa las reglas, primero establece varias listas enlazadas y las variables globales se definen de la siguiente manera.
(en plugbase.c):
KeywordXlateList * KeywordList
lista de palabras clave de preproceso * palabras clave de preproceso;
PreprocessFuncNode * PreprocessList <; /p>
lista de palabras clave de salida * palabras clave de salida;
OutputFuncNode * OutputList
Se omiten las definiciones específicas de estas estructuras. Este proceso de inicialización pone en funcionamiento las claves predefinidas.
Las palabras y las funciones de procesamiento están conectadas en diferentes listas vinculadas según las categorías. Luego, al analizar el archivo de reglas,
si las opciones de la regla contienen palabras clave, la lista vinculada correspondiente se inicializará desde arriba.
Busque y agregue la información necesaria y la funcionalidad de procesamiento al nodo que representa esta regla (usando el tipo RuleTreeNode
, que se describe en detalle a continuación) en un dominio específico (el tipo OptTreeNode). .
Al mismo tiempo, al final de las reglas de inicialización en la función principal, se analiza el archivo de reglas especificado. En la mayoría de los casos
En resumen, hay tres variables globales que contienen las reglas (rules.c):
Alerta de encabezado de lista /*título del bloque de alerta*/
Registro de encabezado de lista;/*Encabezado de bloque de registro*/
Pasar Listerhead;/*Pasar encabezado de bloque*/
Estas variables son de tipo ListHead y, como sugiere el nombre, , representa el encabezado de una lista vinculada. Regístrese en alerta
Para las reglas que deben recibir alertas, las reglas que deben registrarse se registran en el registro y las reglas que se registran en el pase están en.
Ignora el proceso (no se realiza ningún procesamiento). El encabezado de la lista se define de la siguiente manera:
typedef struct _ListHead
{
RuleTreeNode * TcpList
RuleTreeNode * UdpList
RuleTreeNode * IcmpList
} ListHead
Como puede ver, hay tres punteros en cada estructura ListHead, que apuntan al encabezado de la lista vinculada para procesar Tcp/Udp/ Reglas de paquetes icmp. Aquí aparece una nueva estructura RuleTreeNode.
Para ilustrar la relación jerárquica de la lista vinculada, la definición de RuleTreeNode se enumera a continuación, pero la mayoría de los campos se ignoran:
typedef struct _RuleTreeNode
{
RuleFpList * regla _ func
......//Ignorar
estructura _ RuleTreeNode * derecha
OptTreeNode * abajo/*Opciones de regla asociadas con esta Lista
Rule Node*/
} RuleTreeNode
RuleTreeNode contiene los tres campos de puntero anteriores, que pueden formar tres listas vinculadas respectivamente. RuleTreeNode*
El tipo de la derecha apunta al siguiente RuleTreeNode, que es equivalente al siguiente campo en una lista enlazada ordinaria, pero se nombra aquí. Esto forma una lista enlazada regular.
Rule_func, un puntero a la clase RuleFpList, registra la lista vinculada de controladores de reglas. A veces es necesario analizar una regla llamando a varios controladores. Por eso es necesario hacer una lista enlazada. Echemos un vistazo a la
definición. Además del siguiente campo, también hay un puntero de función:
typedef struct _RuleFpList
{
/ *Puntero de función de verificación de reglas*/
int (*RuleHeadFunc)(Packet *, struct _RuleTreeNode *, struct _ RuleFpList *);
/*Puntero al siguiente nodo de función de regla */
struct _ RuleFpList * next
} RuleFpList
El tercer campo de puntero es el puntero hacia abajo de la clase OptTreeNode El comentario después. esta línea indica claramente que se trata de una lista vinculada de opciones de reglas asociadas con este nodo de regla. Desafortunadamente, la estructura de OptTreeNode también es bastante compleja y se introducen varias listas enlazadas nuevas. Ignorando algunos campos, OptTreeNode se define de la siguiente manera:
typedef struct _ OptTreeNode
{
/*Función de complemento/detección aquí*/
OptFpList * opt_func
/* ds_list es absolutamente necesario para que el sistema de complementos funcione,
permite a los autores de complementos asociar estructuras de datos "dinámicas"
Tener un sistema de reglas les permite vincular cualquier cosa que se les ocurra
Con listas de reglas*/
void * ds_list[512] /* Datos del complemento Lista de punteros de estructura*; /
.......//Algunos campos están omitidos.
struct _ OptTreeNode * next
} OptTreeNode
Next apunta al siguiente nodo en la lista vinculada, no hace falta decirlo. El puntero opt_func del tipo OptFpList apunta a una lista vinculada de funciones de opción, que no es muy diferente de RuleFpList mencionada anteriormente. Es de destacar que hay series de punteros.
Ds_list se utiliza para registrar el procesamiento predefinido involucrado en la regla. El tipo de cada elemento es void*. Cuando realmente representa las reglas, ds_list se convierte a diferentes tipos predefinidos.