Ver destacados

Escaner automatico para buscar host activos con Python

Como crear un escáner automático en Python para saber las direcciones IP que están activas en una red
Escrito por
19K Visitas  |  Publicado jun 10 2016 13:14
Favorito
Compartir
Comparte esta pagina a tus Amigos y Contactos usando las siguientes Redes Sociales


¿Te gustaría ver que IPs están activas en una red? ¿Te gustaría saber cómo se realiza un programa de este estilo? Pues hoy te enseño cómo hacer un programa en Python 3 que escaneará la red en un rango de IPs que le proporciona el usuario.

 

Para está tarea vamos a automatizar el ping del sistema operativo.

 

Opción 1 - Escáner simple


Está primera opción la pongo, porque es más sencilla de entender y de llevar a cabo, antes de meternos en algo más complicado.

 

El programa completo es el siguiente:

import os
import sys
import platform
from datetime import datetime

ip = input("Ingresa la IP: ")
ipDividida = ip.split('.')

try:
    red = ipDividida[0]+'.'+ipDividida[1]+'.'+ipDividida[2]+'.'
    comienzo = int(input("Ingresa el número de comienzo de la subred: "))
    fin = int(input("Ingresa el número en el que deseas acabar el barrido: "))
except:
    print("[!] Error")
    sys.exit(1)


if (platform.system()=="Windows"):
    ping = "ping -n 1"
else :
    ping = "ping -c 1"
    
tiempoInicio = datetime.now()
print("[*] El escaneo se está realizando desde",red+str(comienzo),"hasta",red+str(fin))
for subred in range(comienzo, fin+1):
    direccion = red+str(subred)
    response = os.popen(ping+" "+direccion)
    for line in response.readlines():
        if ("ttl" in line.lower()):
            print(direccion,"está activo")
            break
            
tiempoFinal = datetime.now()
tiempo = tiempoFinal - tiempoInicio
print("[*] El escaneo ha durado %s"%tiempo)
[color=#a9a9a9]Código completo[/color]

 

Paso 1

Necesitamos importar unas librerías, para nuestro programa:
import os
import sys
import platform
from datetime import datetime
[color=#a9a9a9]Librerías[/color]

 

Explicación de las librerías
  • os: La necesitamos para realizar el ping a través del sistema operativo.
  • sys: La utilizo para terminar el programa ante un error en la introducción de datos del usuario.
  • platform: Nos permite saber el sistema operativo donde corremos el programa, su uso nos hace independientes de plataformas.
  • datetime: Lo uso para saber el tiempo que tarda en realizar el escaneo, si no quieres saberlo te lo puedes ahorrar.

 

Paso 2

En el siguiente trozo de código le solicitamos al usuario los datos necesarios, como son el host y el rango de la subred. También tenemos un bloque try, catch que lo uso básicamente para terminar el programa de una manera controlada, si la IP que inserta el usuario no es correcta la primera instrucción del bloque dará error, y si al pedirle el comienzo y fin no inserta números saltará un error.
ip = input("Ingresa la IP: ")
ipDividida = ip.split('.')

try:
    red = ipDividida[0]+'.'+ipDividida[1]+'.'+ipDividida[2]+'.'
    comienzo = int(input("Ingresa el número de comienzo de la subred: "))
    fin = int(input("Ingresa el número en el que deseas acabar el barrido: "))
except:
    print("[!] Error")
    sys.exit(1)
La primera instrucción del bloque try la uso para crear un prefijo de la red, que más adelante nos será útil.

 

Por ejemplo en la siguiente imagen con los datos que inserto escanearíamos para ver si están activas las direcciones desde la 192.168.0.190 a la 192.168.0.199.

 

 

Paso 3

En la próxima parte del código lo único que compruebo es que sistema operativo se está utilizando a través de la función platform.system().
if (platform.system()=="Windows"):
    ping = "ping -n 1"
else:
    ping = "ping -c 1"
Esto es necesario porque queremos mandar un solo paquete, y en Windows la instrucción se hace con -n y en unix con -c.

 

Paso 4

A continuación analizaré el siguiente fragmento de código:
tiempoInicio = datetime.now()
print("[*] El escaneo se etsá realizando desde",red+str(comienzo),"hasta",red+str(fin))
for subred in range(comienzo, fin+1):
    direccion = red+str(subred)
    response = os.popen(ping+" "+direccion)
    for line in response.readlines():
	    if ("ttl" in line.lower()):
		    print(direccion,"está activo")
		    break
		   
tiempoFinal = datetime.now()
tiempo = tiempoFinal - tiempoInicio
print("[*] El escaneo ha durado %s"%tiempo)
En este paso es donde llevamos a cabo la verdadera funcionalidad, así que antes de comenzar obtengo la hora correspondiente:
tiempoInicio = datetime.now()
Y pintamos una línea por pantalla para que el usuario sepa que el escaneo se está realizando (y el rango):
print("[*] El escaneo se etsá realizando desde",red+str(comienzo),"hasta",red+str(fin))
Luego vemos un for, que nos va a recorrer el rango de direcciones ip deseadas, su primera instrucción concatena al prefijo de la red los números que nos faltan, es decir si tenemos 192.168.0. entonces si el bucle for va de 190 a 199, la primera vez que entre direccion valdrá 192.168.0.190 y según avance se cambiara el 190, el resto lo mantenemos. Acto seguido obtenemos la respuesta del ping, que lo realiza la instrucción:
os.popen(ping+" "+direccion)
Para saber si la IP está activa comprobaremos si la respuesta que tenemos contiene la palabra ttl, utilizo line.lower() porque parece que en Linux sale en minúsculas y en Windows en mayúsculas, así no tenemos problemas.

 

En la parte final, lo único que hago es obtener de nuevo la hora, y resto esta nueva hora con la anterior para pintar el tiempo que ha tardado mi programa.

 

A continuación muestro una imagen de la ejecución del programa, como vemos es algo lento (52 segundos para 19 direcciones) también depende de la potencia del PC, pero este tiempo se puede mejorar si usamos hilos, así que ahora haré el programa usando los “threads” de Python.

 

 

Opción 2 - Escáner Python con hilos


Vamos a comenzar ahora un programa similar, pero algo más complejo, ya que ahora se va a repartir el trabajo entre varios hilos y no solo se va a quedar la carga uno, al final veremos que el tiempo se reduce mucho, así que podemos decir que es una versión más óptima.

 

El programa es el siguiente:

import os
import sys
import platform
import threading, subprocess
from datetime import datetime

IPXHILOS = 4
ip = input("Ingresa la IP: ")
ipDividida = ip.split('.')

try:
    red = ipDividida[0]+'.'+ipDividida[1]+'.'+ipDividida[2]+'.'
    comienzo = int(input("Ingresa el número de comienzo de la subred: "))
    fin = int(input("Ingresa el número en el que deseas acabar el barrido: "))
except:
    print("[!] Error")
    sys.exit(1)


if (platform.system()=="Windows"):
    ping = "ping -n 1"
else :
    ping = "ping -c 1"
    

class Hilo (threading.Thread):
    def __init__(self,inicio,fin):
        threading.Thread.__init__(self)
        self.inicio = inicio
        self.fin = fin
        
    def run(self):
        for subred in range(self.inicio,self.fin):
            direccion = red+str(subred)
            response = os.popen(ping+" "+direccion)
            for line in response.readlines():
                if ("ttl" in line.lower()):
                    print(direccion,"está activo")
                    break


tiempoInicio = datetime.now()
print("[*] El escaneo se está realizando desde",red+str(comienzo),"hasta",red+str(fin))
NumeroIPs = fin-comienzo
numeroHilos = int((NumeroIPs/IPXHILOS))
hilos = []

try:
    for i in range(numeroHilos):
        finAux = comienzo+IPXHILOS
        if(finAux > fin):
            finAux = fin
        hilo = Hilo(comienzo, finAux)
        hilo.start()
        hilos.append(hilo)
        comienzo = finAux
except Exception as e:
    print("[!] Error creando hilos:",e)
    sys.exit(2)


for hilo in hilos:
    hilo.join()

tiempoFinal = datetime.now()
tiempo = tiempoFinal - tiempoInicio
print("[*] El escaneo ha durado %s"%tiempo)
[color=#a9a9a9]Programa completo[/color]

 

Aquí voy a hablarte de instrucciones que cambian y se añaden (voy a obviar las partes iguales al programa anterior):

 

Los import que usamos en el anterior programa nos valen, solo necesitamos añadir lo siguiente, que se utilizara para los hilos en Python.

import threading, subprocess
Utilizo una variable para el número de IPs que quiero que compruebe cada hilo, así que se añade al principio del programa:
IPXHILOS = 4
La petición de datos al usuario y la comprobación de sistema operativo se mantienen intactas. En este programa creo una clase llamada Hilo que extiende de threading.Thread, está clase recibe como parámetros el inicio y el fin de las direcciones con las que tendrá que trabajar cada hilo, luego tengo una función run, que es necesaria y se tiene que llamar así, se encargara de realizar el trabajo cuando posteriormente iniciemos el hilo, el for no cambia:
class Hilo (threading.Thread):
    def __init__(self,inicio,fin):
	    threading.Thread.__init__(self)
	    self.inicio = inicio
	    self.fin = fin
	   
    def run(self):
	    for subred in range(self.inicio,self.fin):
		    direccion = red+str(subred)
		    response = os.popen(ping+" "+direccion)
		    for line in response.readlines():
			    if ("ttl" in line.lower()):
				    print(direccion,"está activo")
				    break
Ahora vamos a explicar la parte que tengo fuera de la clase Hilo.

 

La siguiente instrucción la utilizo para saber el número de IPs que tengo en total, según el comienzo y el fin que me da el usuario:

NumeroIPs = fin-comienzo
Ahora una vez sabemos esto, podemos calcular el número de hilos que voy a necesitar para trabajar:
numeroHilos = int((NumeroIPs/IPXHILOS))
Necesitare una lista donde almacenar cada hilo, para así después hacer que el hilo principal espere a que terminen el trabajo:
hilos = []
El siguiente fragmento de código va a ir creando los hilos y pasándole su tramo de trabajo, para ello tenemos que ir “jugando” con el comienzo y fin de cada hilo, por eso he creado la variable finAux. Una vez que se crea el hilo se inicia con start() y se añade a la lista de hilos.
try:
    for i in range(numeroHilos):
	    finAux = comienzo+IPXHILOS
	    if(finAux > fin):
		    finAux = fin
	    hilo = Hilo(comienzo, finAux)
	    hilo.start()
	    hilos.append(hilo)
	    comienzo = finAux
except Exception as e:
    print("[!] Error creando hilos:",e)
    sys.exit(2)
A continuación creo un bucle que tiene como finalidad esperar a que los hilos acaben
for hilo in hilos:
    hilo.join()
Y por último se toma la hora, se restaría a la que tomé antes de comenzar y se muestra por pantalla, al igual que el anterior programa.

 

Si hacemos la misma prueba que antes con este programa vemos que tarda 6 segundos en hacer el mismo trabajo, menuda diferencia.

 

 

 

Nota
El tiempo te puede variar dependiendo de la potencia de tu PC y de la variable IPXHILOS, yo la asigne un 4, si asignas más trabajo a cada hilo va a tardar más, si tiene menos trabajo será más rápido, pero ten cuidado que existe un límite en el número de hilos que podemos crear.

 

¿Podemos confiar que este programa nos del 100% de los host activos?
La respuesta es no, ya que se puede bloquear el ping en un host bloqueando las peticiones y/o respuestas ICMP, de lo que sí puedes estar seguro es de que si te dice que está activo, lo está. Existen otros tipos de escáner, como el de TCP que puedes hacerlo con los puertos que normalmente deja abierto un sistema operativo, y la combinación sel escáner TCP y el de ping será más fiable.

 

Os dejo un zip con los 2 códigos:

 

codigos_ping_python.zip   1,38K   451 Descargas

¿Te ayudó este Tutorial?

Ayuda a mejorar este Tutorial!
¿Quieres ayudarnos a mejorar este tutorial más? Puedes enviar tu Revisión con los cambios que considere útiles. Ya hay 0 usuario que han contribuido en este tutorial al enviar sus Revisiones. ¡Puedes ser el próximo!