Red de conocimiento informático - Aprendizaje de código fuente - ¿Cuáles son los ganchos para interceptar paquetes del protocolo http?

¿Cuáles son los ganchos para interceptar paquetes del protocolo http?

El artículo es bastante largo, así que léelo despacio. Reimpreso

El principio de utilizar HOOK para interceptar paquetes API es algo muy útil, por ejemplo, si desea analizar cómo funcionan los programas de otras personas. Aquí presentaré un método que he probado y aprobado. Primero, debemos intentar poner nuestro código en el espacio de proceso del programa de destino. Windows Hook puede ayudarnos a lograrlo. La declaración de SetWindowsHookEx es la siguiente: HHOOK SetWindowsHookEx( int idHook, // tipo de enlace HOOKPROC lpfn, // procedimiento de enlace HINSTANCE hMod, // maneja la instancia de la aplicación DWORD dwThreadId // identificador de subproceso); puedes leer msdn. No hay msdn. Es difícil moverse ni siquiera una pulgada. La función de Hook en sí no es importante aquí. El propósito de usarlo es simplemente permitir que Windows implante nuestro código en otros procesos. Podemos elegir cualquier tipo de gancho, siempre que se garantice que el programa de destino lo llame. Aquí uso WH_CALLWNDPROC. lpfn y hMod apuntan a nuestro código de enlace y el dll donde se encuentra, respectivamente, dwThreadId está configurado en 0, lo que significa que dicho enlace se cuelga en todos los subprocesos del sistema, para que podamos colocar el código en otros procesos. Después de eso, nuestro código ha ingresado a todos los espacios de proceso en el sistema. Cabe señalar que solo necesitamos interceptar llamadas del programa de destino que nos interesa, por lo que también debemos distinguir el número de proceso. En nuestra propia función de enlace, la primera ejecución realizará el trabajo de redirección de API más importante. Es decir, al cambiar los primeros bytes de la API que deben ser interceptados en una instrucción de salto, saltará a nuestra API. Esta es la parte más crítica. Aquí quiero interceptar tres llamadas, enviar y recibir en ws2_32.dll y GetMessageA en user32.dll.

DWORD dwCurrentPID = 0; HHOOK hOldHook = NULL; DWORD pRecv = 0; GETMESSAGE pGetMessage = NULL; BYTE btNewBytes[8] = { 0x0B8, 0x0, 0x40, 0x0, 0x0FF, 0x0E0, 0 }; PALABRA dwOldBytes[3][2]; HANDLE hDebug = INVALID_HANDLE_value; LRESULT CALLBACK CallWndProc( int nCode, WPARAM wParam, LPARAM lParam ) { DWORD dwPIDWatched; ID de acceso() ; HWND hwndMainHook; hwndMainHook = ::FindWindow( 0, "MainHook" ); dwPIDWatched = ::SendMessage( hwndMainHook, (WM_USER+100), 0, 0 ; 101), 0, 0); if( dwCurrentPID == dwPIDWatched) { hLib = LoadLibrary( "ws2_32.dll" ); " recv" ); ::ReadProcessMemory( INVALID_HANDLE_value, (void *)pSend, (void *)dwOldBytes[0], sizeof(DWORD)*2, &dwSize ); new_send ; ::WriteProcessMemory( INVALID_HANDLE_value, (void *)pSend, (void *)btNewBytes, sizeof(DWORD)*2, &dwSize ); ::ReadProcessMemory( INVALID_HANDLE_value, (void *)pRecv, (void *)dwOldBytes[1] , tamaño de (DWORD)*2, &dwSize ); 2, &dwSize); hLib = LoadLibrary( "user32.dll" ); pGetMessage = (GETMESSAGE)GetProcAddress( hLib, "GetMessageA" );

Mensaje, (void *)dwOldBytes[2], sizeof(DWORD)*2, &dwSize ); void *)btNewBytes, sizeof(DWORD)*2, &dwSize); hDebug = ::CreateFile( "C:\\Trace.log", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ); = NULL ) { return CallNextHookEx( hOldHook, nCode, wParam, lParam } return 0 } La función de enlace anterior solo es útil cuando se ejecuta por primera vez, que consiste en modificar los primeros 8 bytes de las tres funciones (en realidad, solo Requiere 7). Las instrucciones en btNewBytes son en realidad mov eax, 0x400000 jmp eax. El 0x400000 aquí es la dirección de la nueva función, como new_recv/new_send/new_GetMessage. Echemos un vistazo a lo que se hace en nuestra función.

Tome GetMessageA como ejemplo: BOOL _stdcall new_GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax ) { DWORD dwSize; char szTemp[256]; BOOL r = false; Antes de GetMessage: HWND 0x%8.8X, msgMin 0x%8.8X, msgMax 0x%8.8x \r\n", hWnd, wMsgFilterMin, wMsgFilterMax); ::WriteFile( hDebug, szTemp, strlen(szTemp), &dwSize, 0) ; // Vigilar // restaurarlo al principio ::WriteProcessMemory( INVALID_HANDLE_value, (void *)pGetMessage, (void *)dwOldBytes[2], sizeof(DWORD)*2, &dwSize ; // ejecutarlo r = pGetMessage( lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax); // enganchelo nuevamente *(DWORD *)( btNewBytes + 1 ) = (DWORD)new_GetMessage; ::WriteProcessMemory( INVALID_HANDLE_value, (void *)pGetMessage, (void *)btNewBytes, sizeof( DWORD )*2, &dwSize ); //Mira aquí después de que se ejecute sprintf( szTemp, "El resultado de GetMessage es %d.\r\n", r ::WriteFile( hDebug, szTemp, strlen( szTemp ), &dwSize); , 0 ); if( r ) { sprintf( szTemp, "Msg : HWND 0x%8.8X, MSG 0x%8.8x, wParam 0x%8.8X, lParam 0x%8.8X\r\nTiempo 0x%8.8X, X % d , Y %d\r\n", lpMsg->hwnd, lpMsg->message, lpMsg->wParam, lpMsg->lParam, lpMsg->time, lpMsg->pt.x, lpMsg->pt.y ) ; / / Vigilar return r; } Primero escriba los parámetros interceptados en un archivo de registro para su análisis.

Luego restaure los primeros 8 bytes de GetMessageA que se retuvieron originalmente y luego ejecute la llamada real a GetMessageA. Una vez completada, los resultados de la ejecución también se escriben en el archivo de registro y luego los resultados de la ejecución de GetMessageA se devuelven a la persona que llama. Todo el proceso de interceptación es así. Puede cambiar la parte de registro a la operación que desee. Una desventaja aquí es que la acción de interceptación no se puede realizar al mismo tiempo. Si el proceso de destino es de subprocesos múltiples, habrá problemas. La solución es agregar un bloqueo y desbloqueo de CriticalSection a cada new_GetMessage para que la llamada se vuelva en serie y los paquetes de datos IP que fluyen a través de la tarjeta de red local sean interceptados en forma de sockets sin procesar y una cantidad considerable de cuasi. -Los piratas informáticos (refiriéndose a aquellos que utilizan software de piratería ya preparado para llevar a cabo ataques en lugar de escribir su propio código según sea necesario) ciertamente no estarán familiarizados con los rastreadores de redes (los rastreadores de redes también juegan un papel importante en la seguridad de la red). un papel muy importante en los ataques de hackers. Al utilizar un rastreador de red, la tarjeta de red se puede configurar en modo promiscuo y los paquetes de datos transmitidos en la red se pueden capturar y analizar. El resultado de este análisis se puede utilizar para el análisis de seguridad de la red, pero si lo utilizan los piratas informáticos, también puede proporcionar información valiosa para lanzar más ataques. Se puede ver que el rastreador es en realidad un arma de doble filo. Aunque la tecnología de rastreo de red representará una cierta amenaza para la seguridad de la red cuando la utilicen los piratas informáticos, el daño del rastreador en sí no es muy grande. Se utiliza principalmente para proporcionar inteligencia de red a otros programas de piratas informáticos. Los ataques reales son causados ​​principalmente por otros Blacksoft. para completarlo. En términos de seguridad de la red, los métodos de rastreo de redes pueden detectar eficazmente la información de los paquetes de datos transmitidos en la red. El análisis y la utilización de esta información ayudarán a mantener la seguridad de la red. Sopesando los pros y los contras, es necesario introducir el principio de implementación del rastreador de red. Texto del artículo Principio de diseño del sniffer Como programa de comunicación de red, el sniffer también implementa la comunicación de red programando la tarjeta de red. La programación de la tarjeta de red también se lleva a cabo utilizando el método de socket habitual. Sin embargo, un programa de socket típico solo puede responder a tramas de datos que coincidan con su propia dirección de hardware o que se envíen en forma de transmisiones para otras formas de tramas de datos, como tramas de datos que han llegado a la interfaz de red pero no se envían. esta dirección, la interfaz de red no provocará una respuesta después de verificar que la dirección de entrega no es su propia dirección, lo que significa que la aplicación no podrá recibir los paquetes que llegan. El propósito del rastreador de red es precisamente recibir todos los paquetes de datos que lo atraviesan desde la tarjeta de red. Estos paquetes de datos pueden enviarse a ella o a otro lugar. Obviamente, para lograr este objetivo, la tarjeta de red ya no puede funcionar en el modo normal habitual, sino que debe configurarse en modo promiscuo. Específicamente para la implementación de programación, esta configuración del modo promiscuo de la tarjeta de red se implementa a través de sockets sin formato (sockets sin formato), que también es diferente de los sockets de flujo de datos y los sockets de datagramas de uso común. Después de crear el socket original, debe configurar las opciones de operación del encabezado IP a través de la función setsockopt() y luego vincular el socket original a la tarjeta de red local a través de la función bind(). Para que el socket original acepte todos los datos, debe configurarse mediante ioctlsocket () y también puede especificar si desea procesar el encabezado IP usted mismo. En este punto, puede comenzar a rastrear paquetes de datos de red. La adquisición de paquetes de datos aún se completa a través de la función recv() como un socket de transmisión o un socket de datagrama. Pero a diferencia de los otros dos sockets, el paquete de datos capturado por el socket original en este momento no es solo información de datos pura, sino la información de datos más original, incluido el encabezado IP, el encabezado TCP y otros encabezados de información. Esta información conserva su apariencia original cuando se transmite. a través de la red. Se puede obtener cierta información sobre la red analizando la información sin procesar transmitida en la capa inferior. Dado que estos datos están empaquetados por la capa de red y la capa de transporte, los paquetes de datos deben analizarse en función de los encabezados de trama adjuntos. La estructura se proporciona a continuación La estructura general del paquete de datos: Paquete de datos Encabezado IP Encabezado TCP (u otro encabezado de información) Datos Cuando los datos llegan a la capa de transporte desde la capa de aplicación, un encabezado de segmento de datos TCP o un encabezado de segmento de datos UDP. se agregará.

El encabezado del segmento de datos UDP es relativamente simple y consta de un encabezado de 8 bytes y una parte de datos. El formato específico es el siguiente: puerto de origen de 16 bits, puerto de destino, longitud UDP, suma de comprobación UDP, mientras que el encabezado de datos TCP es más complejo, con 20 fijos. A partir del byte, puede haber algunas opciones con longitud variable después del encabezado fijo. El formato del encabezado del segmento de datos TCP se proporciona a continuación: 16 bits 16 bits Puerto de origen Puerto de destino Número de secuencia Número de confirmación Longitud del encabezado TCP (reservado) 7 bits URG. ACK PSH RST SYN FIN Puntero de emergencia de suma de verificación de tamaño de ventana opcional (0 o más palabras de 32 bits) Datos (opcional) El análisis de este encabezado de segmento de datos TCP se puede definir a través de la estructura de datos _TCP en la implementación de programación: typedef struct _TCP{ WORD SrcPort ; // Puerto de origen WORD DstPort; // Puerto de destino DWORD SeqNum; // Número de secuencia DWORD AckNum; // Número de confirmación BYTE DataOff // Longitud del encabezado TCP BYTE Banderas (URG, ACK, etc.) ; // Tamaño de ventana WORD Chksum; // Suma de comprobación WORD UrgPtr; // Puntero de emergencia} TCP; typedef TCP *LPTCP; typedef TCP UNALIGNED * ULPTCP también debe agregarse un encabezado de segmento de datos IP; un datagrama IP. El encabezado de datos IP se transmite en orden de punto final grande, de izquierda a derecha, con el byte de orden superior del campo de versión transmitido primero (SPARC es una máquina de punto final grande; Pentium es una máquina de punto final pequeño). Si se trata de una máquina de punto final pequeña, se debe convertir antes de transmitir y recibir. El formato del encabezado del segmento de datos IP es el siguiente: 16 bits Versión de 16 bits Tipo de servicio DIH longitud total identificación indicador segmento compensación duración del protocolo suma de verificación del encabezado dirección de origen dirección de destino opciones (0 o más) De manera similar, en la programación real, una estructura de datos para representa este encabezado de segmento de datos IP La definición de esta estructura de datos se proporciona a continuación: typedef struct _IP{ union{ BYTE Version; // Versión BYTE HdrLen // BYTE ServiceType; longitud WORD ID; // Unión de identificación { WORD Flags; // Bandera WORD FragOff; // Desplazamiento del fragmento}; // Duración del protocolo BYTE // Protocolo WORD HdrChksum; DWORD DstAddr; // Dirección de destino BYTE Opciones; // Opciones} IP; typedef IP * LPIP; typedef IP UNALIGNED * ULPIP; Implementación específica del rastreador Basado en las ideas de diseño anteriores, no es difícil escribir el código de implementación del rastreador de red. Aquí hay un ejemplo simple que puede capturar todos los paquetes de datos que pasan a través de la tarjeta de red local y analizarlos. Dirección IP de origen, dirección IP de destino, número de puerto de origen TCP, número de puerto de destino TCP y longitud del paquete. Dado que el proceso de diseño del programa se ha descrito claramente antes, no entraré en detalles aquí. La implementación específica del programa se explicará a continuación con comentarios. Al mismo tiempo, en aras de la claridad del flujo del programa. Se han eliminado los códigos de protección, como la comprobación de errores.

La lista de implementación del código principal es: // Verifique el número de versión de Winsock, WSAData es el objeto de estructura WSADATA WSAStartup(MAKEWORD(2, 2), &WSAData // Cree el socket original sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)); // Establezca las opciones de operación del encabezado IP, donde el indicador se establece en verdadero, y procese el encabezado IP usted mismo setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag) // Obtenga el nombre local gethostname(; (char* )LocalName, sizeof(LocalName)-1); // Obtenga la dirección IP local pHost = gethostbyname((char*)LocalName) // Complete la estructura SOCKADDR_IN addr_in.sin_addr = *(in_addr *)pHost ->h_addr_list[0] ; //IP addr_in.sin_family = AF_INET; addr_in.sin_port = htons(57274); // Vincula el calcetín del socket original a la dirección de la tarjeta de red local bind(sock, (PSOCKADDR)&addr_in, sizeof(addr_in )); // dwValue es el parámetro de entrada y salida, ejecutado cuando es 1, cancelado cuando 0 DWORD dwValue = 1 // Establezca SOCK_RAW en SIO_RCVALL para recibir todos los paquetes IP. Entre ellos, SIO_RCVALL // se define como: #define SIO_RCVALL _WSAIOW(IOC_VENDOR,1) ioctlsocket(sock, SIO_RCVALL, &dwValue); El trabajo anterior es básicamente configurar el socket original. Después de configurar el socket original, puede funcionar como. Como era de esperar, puede recibir datos de la tarjeta de red a través de la función recv (). El paquete de datos original recibido se almacena en el caché RecvBuf [] y la longitud del búfer BUFFER_SIZE se define como 65535.

Luego, los paquetes de datos capturados se pueden analizar en función de la descripción estructural anterior del encabezado del segmento de datos IP y del encabezado del segmento de datos TCP: while (true) { // Recibir la información del paquete de datos original int ret = recv(sock, RecvBuf, BUFFER_SIZE, 0); if (ret > 0) { // Analiza el paquete de datos y genera el resultado del análisis ip = *(IP*)RecvBuf; tcp = *(TCP*)(RecvBuf + ip.HdrLen(" Protocolo: %s\r\n",GetProtocolTxt(ip.Protocol)); TRACE("Dirección IP de origen: %s\r\n",inet_ntoa(*(in_addr*)&ip.SrcAddr)); TRACE("Dirección IP de destino : %s\r\n",inet_ntoa(*(in_addr*)&ip.DstAddr)); TRACE("Número de puerto de origen TCP: %d\r\n",tcp.SrcPort); TRACE("Número de puerto de destino TCP : %d\r\n",tcp.DstPort); TRACE("Longitud del paquete: %d\r\n\r\n\r\n",ntohs(ip.TotalLen)); } } Donde, al realizar análisis de protocolo se utiliza la función GetProtocolTxt(), la cual se encarga de convertir el protocolo (identificación digital) en el paquete IP en salida de texto. La función se implementa de la siguiente manera: #define PROTOCOL_STRING_ICMP_TXT "ICMP" #define PROTOCOL_STRING_TCP_TXT "TCP" #. define PROTOCOL_STRING_UDP_TXT "UDP" #define PROTOCOL_STRING_SPX_TXT "SPX" #define PROTOCOL_STRING_NCP_TXT "NCP" #define PROTOCOL_STRING_UNKNOW_TXT "DESCONOCIDO" …… CString CSnifferDlg::GetProtocolTxt(int Protocol) { switch (Protocol){ case IPPROTO_ICMP : // 1 /* mensaje de control protocolo */ return PROTOCOL_STRING_ICMP_TXT; case IPPROTO_TCP : //6 /* tcp */ return PROTOCOL_STRING_TCP_TXT; case IPPROTO_UDP : //17 /* protocolo de datagrama de usuario */ return PROTOCOL_STRING_UDP_TXT; exitoso Para compilar, debe incluir los archivos de encabezado winsock2.h y ws2tcpip.h. En este ejemplo, los resultados del análisis se generan utilizando la macro TRACE() y se ejecutan en el estado de depuración. El resultado del análisis obtenido es el siguiente: Protocolo: UDP Dirección IP de origen: 172.168.1.5 Dirección IP de destino: 172.168.1.255 Puerto de origen TCP. número: 16707 Número de puerto de destino TCP: 19522 Longitud del paquete: 78... Protocolo: TCP Dirección de origen IP: 172.168.1.17 Dirección IP de destino: 172.168.1.1 Número de puerto de origen TCP: 19714 Número de puerto de destino TCP: 10 Longitud del paquete: 200. .. Se puede ver en los resultados del análisis que este programa posee completamente las funciones básicas de captura de datos de sniffer y análisis de paquetes.

Resumen El método de captura de datos de red en modo socket sin formato presentado en este artículo es relativamente simple de implementar. En particular, la captura de paquetes se puede lograr sin escribir un controlador de dispositivo virtual VxD, lo que hace que el proceso de escritura sea muy simple. capturar El encabezado del paquete de datos recibido no contiene información de trama, por lo que no se pueden recibir otros paquetes de datos que pertenecen a la misma capa de red que IP, como paquetes de datos ARP, paquetes de datos RARP, etc. En el programa de ejemplo proporcionado anteriormente, considerando los factores de seguridad, no se realizó ningún análisis adicional de los paquetes de datos, sino que solo se proporcionó el método de análisis de la información general. A través de la introducción de este artículo, podrá tener una comprensión básica del uso de sockets sin formato y los principios de la estructura del protocolo TCP/IP.