I am trying to build a HTTP server in python,
that sniffs packets and sends them to an other interface.
the server can get routing paths through a POST http request.
So that I need that the server will parallely sniff pakets and listen to http requests.
this is my code:
from scapy.all import *
from scapy.layers.inet import IP, UDP
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
from socketserver import ThreadingMixIn
import threading
ROUTING_LIST = []
INTERFACE_TO_SNIFF = 'vEthernet'
PORT = 80
class Route:
def __init__(self):
self.first_IP_src = ""
self.first_port_src = ""
self.first_IP_dst = ""
self.first_port_dst = ""
self.first_iface = ""
self.second_IP_src = ""
self.second_port_src = ""
self.second_IP_dst = ""
self.second_port_dst = ""
self.second_iface = ""
class Server(BaseHTTPRequestHandler):
# POST echoes the message adding a JSON field
def do_POST(self):
# read the message and convert it into a python dictionary
length = int(self.headers['Content-length'])
message = self.rfile.read(length)
routing_dict = json.loads(message, strict=False)
if add_routing_http(routing_dict) is True:
print("New Routing received:")
print("{" + "\n".join("{!r}: {!r},".format(k, v) for k, v in routing_dict.items()) + "}")
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(
("POST routing request received! now we have " + str(len(ROUTING_LIST)) + " routes").encode("utf8"))
def run_server():
global PORT
server_address = ('', PORT)
httpd = HTTPServer(server_address, Server)
print('Starting httpd on port %d...' % PORT)
httpd.serve_forever()
def add_routing_local(first_IP_src, first_port_src, first_IP_dst, first_port_dst, first_iface,
second_IP_src, second_port_src, second_IP_dst, second_port_dst, second_iface):
global ROUTING_LIST
temp = Route()
temp.first_IP_src = first_IP_src
temp.first_port_src = first_port_src
temp.first_IP_dst = first_IP_dst
temp.first_port_dst = first_port_dst
temp.first_iface = first_iface
temp.second_IP_src = second_IP_src
temp.second_port_src = second_port_src
temp.second_IP_dst = second_IP_dst
temp.second_port_dst = second_port_dst
temp.second_iface = second_iface
ROUTING_LIST.append(temp)
def add_routing_http(routing_dict):
global ROUTING_LIST
temp = Route()
temp.first_IP_src = routing_dict.get('firstIpSrc')
temp.first_port_src = routing_dict.get('firstPortSrc')
temp.first_IP_dst = routing_dict.get('firstIpDst')
temp.first_port_dst = routing_dict.get('firstPortDst')
temp.first_iface = routing_dict.get('firstIface')
temp.second_IP_src = routing_dict.get('secondIpSrc')
temp.second_port_src = routing_dict.get('secondPortSrc')
temp.second_IP_dst = routing_dict.get('secondIpDst')
temp.second_port_dst = routing_dict.get('secondPortDst')
temp.second_iface = routing_dict.get('secondIface')
ROUTING_LIST.append(temp)
return True
def packets_filter(packet):
return IP in packet and UDP in packet and Raw in packet
def match_packet(packet, routing):
match = True
if routing.first_IP_src != '' and packet[IP].src != routing.first_IP_src:
return False
if routing.first_IP_dst != '' and packet[IP].dst != routing.first_IP_dst:
return False
if routing.first_port_src != '' and packet[UDP].sport != routing.first_port_src:
return False
if routing.first_port_dst != '' and packet[UDP].dport != routing.first_port_dst:
return False
if routing.first_iface != '' and packet.sniffed_on is not None and routing.first_iface != packet.sniffed_on:
return False
return True
def handle_packet(packet):
global ROUTING_LIST
for routing in ROUTING_LIST:
if match_packet(packet, routing) is True:
new_packet = packet.copy()
new_packet[IP].src = routing.second_IP_src
new_packet[IP].dst = routing.second_IP_dst
new_packet[UDP].sport = routing.second_port_src
new_packet[UDP].dport = routing.second_port_dst
new_packet.show()
sendp(new_packet) # sendp(new_packet, iface=routing.second_iface)iface='eth0'
return
def main():
daemon = threading.Thread(name='daemon_server', target=run_server, args=())
daemon.setDaemon(True) # Set as a daemon so it will be killed once the main thread is dead.
daemon.start()
print("start sniffing")
sniff(lfilter=packets_filter, prn=handle_packet) # sniff(lfilter=packets_filter, prn=handle_packet, iface=INTERFACE_TO_SNIFF)
if __name__ == "__main__":
main()
In short - I wantthe main function to run in parallel both of functions: run_server, sniff. if I try to run inly one of them - both work great.
In this code only the run_server works but not the sniffing.
What is wrong?
thank you
You have created Thread only for the run_server method. In order to run the sniff function on multithreaded, you will have to create a thread for the sniff function too.
You can learn about basic multithreading from this document:
https://www.geeksforgeeks.org/multithreading-python-set-1/
I wanted to demonstrate Asynchronous Non-Blocking threads through an EventLoop using a chatroom that I coded out in Python.
The chatroom is working fine when I simulate a server and clients on my desktop, but whenever I want to send packets over the internet to my friends who stay at a geographically distant location, I receive Timeout errors.
Obviously, I change the IP_ADDR accordingly while doing so. In fact, I have tried both IPv4 and IPv6. The firewall is off, and there are no anti-viruses installed.
I tried setting the timeout options as well, but the problem still exists.
Over a small geographical distance, the connection works.
I have also checked whether I am even able to send packets over the Internet at all to the target computer using the tracert command, and it seems like I can.
Server.py
import sys
import select
import msvcrt
import socket
IP_ADDR = socket.gethostbyname(socket.gethostname())
PORT = 5555
HEADER_SIZE = 10
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((IP_ADDR, PORT))
def add_header(username, msg):
username = f'{len(username) :< {HEADER_SIZE}}' + username
msg_len = len(msg)
msg = username + f'{msg_len :< {HEADER_SIZE}}' + msg
return msg.encode("utf-8")
sock_list = [server]
sock_dict = {server : 'Server'}
def broadcast_message(client, broadcast_msg):
try: #EAFP
client.send(broadcast_msg)
except:
username = sock_dict[client]
del sock_dict[client]
sock_list.remove(client)
broadcast_msg = add_header(sock_dict[server], f"{username} has left the group!!")
for clients in sock_list:
if clients is server:
print(f"{username} has left the group!!")
else:
broadcast_message(clients, broadcast_msg)
server.listen()
while True:
readers, _, err_sockets = select.select(sock_list, [], [], 1)
if(msvcrt.kbhit()):
msg = input("[:] >> ")
#msg = sys.stdin.readline()[:-1]
msg = add_header(sock_dict[server], msg)
for client in sock_list:
if client is server:
continue
else:
broadcast_message(client, msg)
for reader in readers:
if reader is server:
client_socc, client_addr = server.accept()
try:
client_username = client_socc.recv(1024).decode("utf-8")
if not len(client_username):
continue
else:
print(f"Connection accepted from {client_username[HEADER_SIZE : ].title()} : {client_addr[0]} : {client_addr[1]}")
sock_dict[client_socc] = client_username[HEADER_SIZE : ].title()
sock_list.append(client_socc)
broadcast_msg = add_header(sock_dict[server], f"{sock_dict[client_socc]} has joined the group!!")
for client in sock_list:
if client is server or client is client_socc:
continue
else:
broadcast_message(client, broadcast_msg)
except:
continue
else:
try:
client_msg = reader.recv(1024).decode("utf-8")
if not len(client_msg):
del sock_dict[reader]
sock_list.remove(reader)
else:
while len(client_msg):
broadcast_msg = add_header(sock_dict[reader], client_msg[HEADER_SIZE : HEADER_SIZE + int(client_msg[:HEADER_SIZE])])
print(f"{sock_dict[reader]} >> {client_msg[HEADER_SIZE : HEADER_SIZE + int(client_msg[:HEADER_SIZE])]}")
client_msg = client_msg[HEADER_SIZE + int(client_msg[:HEADER_SIZE]) : ]
for client in sock_list:
if client is server or client is reader:
continue
else:
broadcast_message(client, broadcast_msg)
except:
continue
Client.py
import sys
import select
import socket
import msvcrt
IP_ADDR = socket.gethostbyname(socket.gethostname())
PORT = 5555
HEADER_SIZE = 10
class Connection():
def __init__(self, default = (IP_ADDR, PORT)):
self.client_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print(f"Trying to connect to {default[0]} : {default[1]}")
self.client_conn.connect(default)
print("Connection succesful!")
username = input("Enter your username : ")
username = f'{len(username) :< {HEADER_SIZE}}' + username
self.client_conn.send(username.encode("utf-8"))
def fileno(self):
return self.client_conn.fileno()
def on_read(self):
msg = self.client_conn.recv(1024).decode("utf-8")
self.decode_message(msg)
def decode_message(self, msg):
while len(msg):
username = msg[HEADER_SIZE : HEADER_SIZE + int(msg[: HEADER_SIZE])]
msg = msg[HEADER_SIZE + int(msg[: HEADER_SIZE]) : ]
user_msg = msg[HEADER_SIZE : HEADER_SIZE + int(msg[: HEADER_SIZE])]
msg = msg[HEADER_SIZE + int(msg[: HEADER_SIZE]) : ]
print(f"{username} >> {user_msg}")
class Input():
def __init__(self, client):
self.client = client.client_conn
def fileno(self):
return sys.stdin.fileno()
def on_read(self):
#msg = sys.stdin.readline()[:-1]
msg = input("[:] >> ")
msg_len = len(msg)
msg = f'{msg_len :< {HEADER_SIZE}}' + msg
self.client.send(msg.encode("utf-8"))
connection = Connection()
read_input = Input(connection)
while True:
readers, _, _ = select.select([connection], [], [], 1)
if(msvcrt.kbhit()):
readers.append(read_input)
for reader in readers:
reader.on_read()
When I run the admin client after connecting some clients, my admin returns the ip addresses and port numbers fine. If i close the admin and rerun it nothing happens. This has me baffled. I am unsure why it is doing this
#Admin Client
from functools import partial
import ssl
import socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ts = ssl.wrap_socket(s, certfile="100298750.crt",
keyfile="100298750.key",
ca_certs="5cc515-root-ca.cer")
ts.connect(('192.168.0.5', 4001))
ts.send("Hello\r\n".encode())
if ts.recv(80).decode() == "Admin-Greetings\r\n":
print("The players currently online are:\n")
ts.send("Who\r\n".encode())
for data in iter(partial(ts.recv, 1000), b''):
print(data.decode())
ts.close()
Server
import threading
import socket
import math
import random
import ssl
addressList = []
def within(guess,goal,n):
absValue = abs(guess - goal)
if absValue <= n:
return True
else:
return False
def HandleAdmin(adminSocket,):
while True:
global addressList
(c,a) = adminSocket.accept()
ts = ssl.wrap_socket(c, certfile="5cc515_server.crt",
keyfile="5cc515_server.key",
server_side=True,
cert_reqs=ssl.CERT_REQUIRED,
ca_certs="5cc515-root-ca.cer")
if ts.recv(80).decode() == 'Hello\r\n':
ts.send('Admin-Greetings\r\n'.encode())
if ts.recv(80).decode() == 'Who\r\n':
for i in addressList:
ts.send(i.encode())
ts.close()
return
def HandleClient(c,a):
global addressList
address, port = a
address = str(address) + ' ' + str(port) + '\r\n'
addressList.append(address)
scoreCount = 0
guess = 0
if(c.recv(80).decode()) == 'Hello\r\n':
c.send('Greetings\r\n'.encode())
goal = random.randrange(1,21)
while guess!= goal:
guess =c.recv(80).decode()
guess = int(guess[7:len(guess)-2])
if guess == goal:
c.send('Correct\r\n'.encode())
addressList.remove(address)
c.close()
elif within(guess, goal, 2) == True:
c.send('Close\r\n'.encode())
else:
c.send('Far\r\n'.encode())
else:
c.close()
return
clientSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
clientSocket.bind(("192.168.0.5",4000))
clientSocket.listen(5)
adminSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
adminSocket.bind(("192.168.0.5",4001))
adminSocket.listen(5)
handleAdminThread = threading.Thread(target = HandleAdmin,
args = (adminSocket,))
handleAdminThread.start()
while True:
(c,a) = clientSocket.accept()
clientThread = threading.Thread(target = HandleClient, args = (c,a))
clientThread.start()
If i close the admin and rerun it nothing happens. This has me
baffled. I am unsure why it is doing this
It is simply doing this because the HandleAdmin() server thread code has the line
return
at the end of its loop and thus exits after one run. Drop it, and it is alright.
All my clients sockets do the same thing: send a package every second(22 bytes)
Server code as below:
import select
import socket
import datetime
SList = []
class Tserver:
def __init__(self, portNum):
host = '127.0.0.1'
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind((host, portNum))
self.server.listen(1)
def GETPACK():
# function for CRC check
def CRC(DATA_STR):
return 1
# generate 100 sockets to listen
for x in range(100):
SList.append(Tserver(x+10000))
inputs = []
# put in inputs
for x in range(100):
inputs.append(SList[x].server)
while(True):
ready_socks, _, _ = select.select(inputs, [], [])
for sock in ready_socks:
c, addr = sock.accept()
while(True):
data = c.recv(22)
if len(data) == 22: # To make sure the data-length is 22
# Turn the pack string into bytearray
data_bytes = bytearray()
data_bytes.extend(data)
if CRC(data_bytes) == 1:
print "Connected from client IP Address:" + str(addr)
# ID
ID = 256*data_bytes[1] + data_bytes[2]
print "ID: ", ID
now = datetime.datetime.now()
print "now: ", str(now)
if __name__ == "__main__":
GETPACK()
My server can only print the packages sent by the first connected socket.
And my question is how to print out all message from each ports whenever a package is sent to the server.
See this PyMOTW entry for a detailed explanation of how to use the select module to write a select-based server.
The main differences between that example and your code are:
You just create one listening socket - server. There is no need to listen on multiple ports.
The variable inputs will be a list consisting of server and any other open socket connections to clients.
Your service loop will look like:
while true:
readable, _, _ = select.select(inputs, [], [])
for r in readable:
if r is server:
# handle a new incoming connection
# this will add an entry to the variable inputs
else:
# read some data from socket r and process it
When you attempt to read from a client socket and get an EOF condition, you can close that socket and remove it from the inputs variable.
#ErikR Thanks for your help, i changed my code, and it worked fine.
The reason that my code doesn't work was because of two things:
1.I only create one connection to recv data from my clients.
2.The same connection can't be accepted again for recv, if the clients does't reconnect.(my code doesn't check the exception when clients shutdown)
Code as below:
import select, socket, datetime
SList = []
SconnList = []
class Tserver:
def __init__(self, portNum):
host = '127.0.0.1'
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
self.server.bind((host,portNum))
self.server.listen(1)
print "Server ports: "+str(portNum)
class Sconn:
def __init__(self, sock):
self.conn, self.addr = sock.accept()
def GETPACK():
# function for CRC check
def CRC(DATA_STR):
return 1
# generate 100 sockets to listen
for x in range(100):
SList.append(Tserver(x+10000))
inputs = []
# put in inputs
for x in range(100):
inputs.append(SList[x].server)
while(True):
ready_socks,_,_ = select.select(inputs, [], [])
for sock in ready_socks:
try:
SconnList.append(Sconn(sock))
SconnList.reverse()
inputs.append(SconnList[0].conn)
except:
data = sock.recv(22)
if len(data) == 22: # To make sure the data-length is 22
#Turn the pack string into bytearray
data_bytes = bytearray()
data_bytes.extend(data)
if CRC(data_bytes) == 1:
print "IP Address:" + str(sock.getsockname())
#ID
ID = 256*data_bytes[1] + data_bytes[2]
print "ID: ",ID
now = datetime.datetime.now()
print "now: ",str(now)
print ""
print ""
if __name__ == "__main__":
GETPACK()
I'm trying to create a packet sniffer using pcapy and impacket. I'm stuck with data extraction phase. Unfortunately impacket is not properly documented. At least i could n't find one. Could anyone tel me where to find the documentation or what functions i could use to extract data from captured packet?
edit
my current code
import datetime
import pcapy
import sys
from impacket.ImpactPacket import *
from impacket.ImpactDecoder import *
def main(argv):
dev='ppp0'
print "Sniffing device " + dev
cap = pcapy.open_live(dev , 65536 , 1 , 0)
while(1) :
try:
(header, packet) = cap.next()
eth= LinuxSLLDecoder().decode(packet)
ip=eth.child() #internet layer
trans=ip.child()#transport layer
try:
print 'protocol=',
if ip.get_ip_p() == UDP.protocol:
print 'UDP'
if ip.get_ip_p() == TCP.protocol:
print 'TCP','port=',trans.get_th_dport()
print trans.child()
if ip.get_ip_p() == ICMP.protocol:
print 'ICMP'
print 'src=',ip.get_ip_src(),'dest=',ip.get_ip_dst()
print ''
except:
pass
except pcapy.PcapError:
continue
if __name__ == "__main__":
main(sys.argv)
Sample Output
src= xxx.xxx.xxx.xx dest= xx.xxx.xx.xx
protocol= TCP port= 443
1703 0300 2400 0000 0000 0000 07e2 a2a5 ....$...........
09fe 5b15 3cf1 803d 0c83 8ada 082e 8269 ..[.<..=.......i
0007 8b33 7d6b 5c1a 01 ...3}k\..
What i want to do is extract more data, For example extract the url (if there is a url in packet)
Here is an example for a syn-port scanner with pcap and python and impacket.
Maybe you can tak the important parts out of it.
'''
synscan.py ...
see scan.py for parameters
this works extremely well an a windows that likes to communicate
scanning hosts in same ethernet is possible
scanning host not within the same ethernet may success but does not need to
many algorithms were tried
- raw socket support needs higher previleges
and is impossible because windows does not allow to sniff with them
or to submit sniffable packets
-> not implemented here
"Why do you need special libraries for TCP-SYN scans?"
thats why.
using pcap the program is devided into phases
usually it succeeds in phase 1.
phase 0:
add targets and phase 1
phase 1+: (parallel)
send arp request to resolve target
bombard it with the right packets
sniff
phase 2:
send out udp to resolve mac address by sniffing
send out raw socket tcp syn requests (need higher previleges) optional
phase 3:
if not yet succeeded in phase 1: = mac not found
bombard all macs with packets
phase 4:
bombard broadcasting [mac ff:ff:ff:ff:ff:ff] with packets
phase 5:
clean up - no use
use DEBUG_PHASE to show phases
currently only ipv4 is supported
'''
import sys
import time
import thread
import pcap # pcapy
import impacket
import random
import impacket.ImpactDecoder as ImpactDecoder
import impacket.ImpactPacket as ImpactPacket
import array
import scan
from scan import *
DEFAULT_SOCKET_TIMEOUT = 20
NOTIFY_TIMEOUT = 2
# argument incdeces for socket.socket(...)
SOCK_INIT_FAMILY = 0
SOCK_INIT_TYPE = 1
SOCK_INIT_PROTO = 2
STATE_STATE = 1
STATE_TIME = 0
PCAP_ARGS = ()
PCAP_KW = dict(promisc = True, timeout_ms = 0)
DEBUG = False
DEBUG_IFACE = False and DEBUG # put out which devices are set up
DEBUG_IP = False and DEBUG # print ip debug output for ip packets v4
DEBUG_ARP = False and DEBUG # send arp communication debug out
DEBUG_SYN = False and DEBUG # print out the syn requests sent
DEBUG_PACKET = False and DEBUG # packet inspection as seen by scanner
DEBUG_PHASE = True and DEBUG # scanner phases - 5
DEBUG_STATE = False and DEBUG # debug output about the state
DEBUG_PHASE2 = False and DEBUG # debug output about what is sent in phase 2
# you need higher previleges for some of these operations
ETHER_BROADCAST = (0xff,) * 6 # mac ff:ff:ff:ff:ff:ff
# --- Conversions --------------------------------------------------------------
def ip_tuple(ip):
'''Decode an IP address [0.0.0.0] to a tuple of bytes'''
return tuple(map(int, ip.split('.')))
def tuple_ip(ip):
'''Encode a a tuple of bytes to an IP address [0.0.0.0]'''
return '.'.join(map(str, (ip[0], ip[1], ip[2], ip[3])))
# --- Packet Creation --------------------------------------------------------------
def generate_empty_arp_request():
# build ethernet frame
eth = ImpactPacket.Ethernet()
eth.set_ether_type(0x0806) # this is an ARP packet
eth.set_ether_dhost(ETHER_BROADCAST)# destination host (broadcast)
# build ARP packet
arp = ImpactPacket.ARP()
arp.set_ar_hrd(1)
arp.set_ar_hln(6) # ethernet address length = 6
arp.set_ar_pln(4) # ip address length = 4
arp.set_ar_pro(0x800) # protocol: ip
arp.set_ar_op(1) # opcode: request
arp.set_ar_tha(ETHER_BROADCAST) # target hardware address (broadcast)
eth.contains(arp)
return eth, arp
def generate_empty_ip_packet():
eth = ImpactPacket.Ethernet()
#### values to be set:
# type, shost, dhost
eth.set_ether_type(0x800)
ip = ImpactPacket.IP()
#### values to be set:
# version, IHL, TOS, total_length, ID, Flags, Fragment offset,
# TTL, Protocol, Checksum, source_addr, destination_addr, options
ip.set_ip_v(4)
ip.set_ip_hl(5) # 5 * 32 bit
ip.set_ip_tos(0) # usal packet -> type of service = 0
# total_length
ip.set_ip_id(random.randint(1, 0xffff))
ip.set_ip_df(0) # flags redundant
ip.set_ip_off(0)
ip.set_ip_ttl(250)
ip.set_ip_p(6) # tcp = 6
eth.contains(ip)
return eth, ip
# --- Scanner --------------------------------------------------------------
def start_scan(timeout):
'''return a scanner object
'''
# mac addresses are used to send ethernet packages
mac_addresses = {} # ip : set([mac])
# threadsave access to the targets
targets_lock = thread.allocate_lock()
targets = [] # (family, (ip, port, ...))
# list of target names
target_hosts = set() # host ips
def is_target(host):
return host in target_hosts
def add_target(family, address):
target_hosts.add(address[IP])
mac_addresses.setdefault(address[IP], set())
with targets_lock:
targets.append((family, address))
def store_ip_mac_resolution_for(host):
for family, socktype, proto, canonname, address in \
socket.getaddrinfo(host, 0):
mac_addresses.setdefault(address[IP], set())
def associate_ip_mac(ip, mac):
if ip in mac_addresses or is_target(ip):
if type(mac) is list:
hashable_array_constructor = ('B', ''.join(map(chr, mac)))
else:
hashable_array_constructor = (mac.typecode, mac.tostring())
mac_addresses[ip].add(hashable_array_constructor)
def get_macs(host):
macs = set()
empty_set = set()
for family, socktype, proto, canonname, (ip, port) in \
socket.getaddrinfo(host, 0):
macs.update(mac_addresses.get(ip, empty_set))
return [array.array(*mac) for mac in macs]
def get_local_macs():
macs = set()
for ip in get_host_ips():
for mac in get_macs(ip):
macs.add((ip, tuple(mac.tolist())))
return macs
def ip_known(ip):
return bool(mac_addresses.get(ip, False))
def save_ip_mac_resolution(ether, ip_header):
source_ip = ip_header.get_ip_src()
source_mac = ether.get_ether_shost()
associate_ip_mac(source_ip, source_mac)
destination_ip = ip_header.get_ip_dst()
destination_mac = ether.get_ether_dhost()
associate_ip_mac(destination_ip, destination_mac)
## parse data directly from pcap
def find_connection_response(data):
# Parse the Ethernet packet
decoder = ImpactDecoder.EthDecoder()
find_connection_response_ethernet(decoder.decode(data))
def find_connection_response_ethernet(ether):
eth_type = ether.get_ether_type()
if eth_type == 0x800:
# Received an IP-packet (2048)
# Parse the IP packet inside the Ethernet packet
find_connection_response_ip(ether, ether.child())
elif eth_type == 0x0806:
store_mac_of_target(ether)
## arp response handling
def store_mac_of_target(ether):
arp = ether.child()
if arp.get_ar_op() in (2, ):
if DEBUG_ARP:print 'response'
# Received ARP Response
source_mac_addr = arp.get_ar_sha()
source_ip_addr = tuple_ip(arp.get_ar_spa())
destination_mac_addr = arp.get_ar_tha()
destination_ip_addr = tuple_ip(arp.get_ar_tpa())
if DEBUG_ARP:print source_mac_addr, source_ip_addr, destination_mac_addr, destination_ip_addr
if is_target(destination_ip_addr):
if DEBUG_ARP:print 'intersting:', destination_ip_addr, destination_mac_addr
associate_ip_mac(destination_ip_addr, destination_mac_addr)
if is_target(source_ip_addr):
if DEBUG_ARP:print 'intersting:', source_ip_addr, source_mac_addr
associate_ip_mac(source_ip_addr, source_mac_addr)
## tcp syn-ack response handling
def find_connection_response_ip(ether, ip_header):
save_ip_mac_resolution(ether, ip_header)
if ip_header.get_ip_p() == 0x6:
# Received a TCP-packet
# Parse the TCP packet inside the IP packet
if DEBUG_IP > 2:
print 'received ip packet: %s to %s' % (ip_header.get_ip_src(), \
ip_header.get_ip_dst())
source_ip = ip_header.get_ip_src()
destination_ip = ip_header.get_ip_dst()
if not is_target(source_ip):
return
if DEBUG_IP > 1:print 'found interest in: %s' % ip_header.get_ip_src()
find_connection_response_tcp(ip_header, ip_header.child())
def find_connection_response_tcp(ip_header, tcp_header):
# Only process SYN-ACK packets
source_ip = ip_header.get_ip_src()
source_port = tcp_header.get_th_sport()
destination_ip = ip_header.get_ip_dst()
destination_port = tcp_header.get_th_sport()
print targets
if tcp_header.get_SYN() and tcp_header.get_ACK():
# Get the source and destination IP addresses
# Print the results
if DEBUG_IP: print("Connection attempt %s:(%s) <- %s:%s" % \
(source_ip, source_port, \
destination_ip, destination_port))
if source_ip in target_hosts:
put_port(source_port)
elif tcp_header.get_SYN() and not tcp_header.get_ACK() and source_ip in get_host_ips():
# someone sent a syn request along
# asuming the acknoledge will come here, too
target = (socket.AF_INET, (destination_ip, destination_port))
if DEBUG_IP: print("Connection attempt %s:(%s) --> %s:%s" % \
(source_ip, source_port, \
destination_ip, destination_port))
with targets_lock:
try:
targets.remove(target)
except ValueError:
pass
def put_port(port):
sys.stdout.write(str(port) + '\n')
## syn packet sending
def send_syn(family, addr):
if family == socket.AF_INET:
send_syn_ipv4(addr)
elif family == socket.AF_INET6:
pass
else:
sys.stderr.write('Warning: in send_syn: family %s not supported\n' \
% family)
def send_syn_ipv4(address):
for packet in iter_syn_packets(address):
if DEBUG_PACKET:
print 'packet', id(packet)
send_packet(packet)
def iter_syn_packets(address):
for tcp in iter_tcp_packets(address):
for eth, ip in iter_eth_packets(address):
ip.contains(tcp)
packet = eth.get_packet()
yield packet
def get_host_ips():
return socket.gethostbyname_ex(socket.gethostname())[2]
def iter_eth_packets((target_ip, port)):
eth, ip = generate_empty_ip_packet()
for source_ip in get_host_ips():
ip.set_ip_src(source_ip)
ip.set_ip_dst(target_ip)
for source_mac in get_macs(source_ip):
eth.set_ether_shost(source_mac)
for target_mac in get_macs(target_ip):
eth.set_ether_dhost(target_mac)
yield eth, ip
def get_devices():
return scanning.values()
def iter_tcp_packets((_, target_port)):
tcp = ImpactPacket.TCP()
#### values to set:
# source port, destination port, sequence number, window, flags
source_port = random.randint(2048, 0xffff)
tcp.set_th_sport(source_port)
tcp.set_th_dport(target_port)
tcp.set_th_seq(random.randint(1, 0x7fffffff))
tcp.set_th_win(32768) # window -> discovered this as default
tcp.set_SYN()
yield tcp
# waiting and scanner interaction
keep_running = [1] # True
def wait():
if keep_running:
keep_running.pop() # keep_running = False
while scanning:
time.sleep(0.01)
## raw_input()
def add_scan((socketargs, addr)):
ip = addr[IP]
port = addr[PORT]
family = socketargs[SOCK_INIT_FAMILY]
if ip_known(ip):
send_syn(family, addr)
else:
add_target(family, addr)
notify(family, addr)
notified = {}
def notify(family, addr):
now = time.time()
if family == socket.AF_INET:
ip = addr[IP]
if notified.get(ip, 0) < now - NOTIFY_TIMEOUT:
notified[ip] = now
send_who_is_ipv4(ip)
elif family == socket.AF_INET6:
pass
else:
raise ValueError('unknown protocol family type %i' % family)
scanning_lock = thread.allocate_lock()
scanning = {} # device_name : device
def send_who_is_ipv4(target_ip):
eth, arp = generate_empty_arp_request()
arp.set_ar_tpa(ip_tuple(target_ip)) # target protocol address
for ip, mac in get_local_macs():
arp.set_ar_spa(ip_tuple(ip)) # source protocol address
arp.set_ar_sha(mac) # source hardware address
eth.set_ether_shost(mac) # source hardware address
if DEBUG_ARP: print 'send_who_is_ipv4: %s%s -> %s' % (ip, mac, target_ip)
send_packet(eth.get_packet())
def send_packet(packet):
t = -time.time()
for device in get_devices():
if DEBUG_PACKET:print device, repr(packet)
device.sendpacket(packet)
t -= time.time() - 0.002
if t > 0:
time.sleep(t)
def scan(device_name, device):
if DEBUG_IFACE: print 'dev up: %s' % device_name
with scanning_lock:
if device_name in scanning:
return
scanning[device_name] = device
try:
while device_name in scanning:
time, data = next(device)
find_connection_response(str(data))
finally:
with scanning_lock:
scanning.pop(device_name, None )
if DEBUG_IFACE: print 'dev down: %s' % device_name
def start_scans():
for device_name in pcap.findalldevs():
start_scan(device_name)
start_scan(pcap.lookupdev())
def start_scan(device_name):
device = pcap.pcap(device_name, *PCAP_ARGS, **PCAP_KW)
thread.start_new(scan, (device_name, device))
def notify_loop():
targets_lock.acquire()
while targets or phase:
targets_lock.release()
try:
do_notify()
except:
traceback.print_exc()
# iterate over scanner phases
try:
phases[0]()
except:
traceback.print_exc()
targets_lock.acquire()
targets_lock.release()
def get_state():
return len(targets)
last_state = [time.time(), get_state()]
def state_has_not_changed_for(timeout):
now = time.time()
state = get_state()
if state != last_state[STATE_STATE]:
last_state[STATE_TIME] = now
last_state[STATE_STATE] = state
if DEBUG_STATE: print 'state old:', last_state[STATE_TIME] + timeout < now
return last_state[STATE_TIME] + timeout < now
def reset_state():
now = time.time()
state = get_state()
last_state[STATE_TIME] = now
last_state[STATE_STATE] = state
target_save = [] # needed between phase 3 and 4
phases = []
phase = phases.append
#phase
def do_scanner_phase():
# wait for wait()
if keep_running: return
if DEBUG_PHASE: print 'initiated phase 1 = waiting'
reset_state()
phases.pop(0)
if not targets:
give_up()
#phase
def do_scanner_phase():
# wait to timeout without exiting wait
# send ip packets to the host to enable
if not state_has_not_changed_for(timeout): return
if DEBUG_PHASE: print 'initiated phase 2 = send packets'
send_packets_to_addresses_to_sniff_mac()
reset_state()
phases.pop(0)
if not targets:
give_up()
#phase
def do_scanner_phase():
# wait to timeout without exiting wait
# set all ip hosts to have all mac addresses
if not state_has_not_changed_for(timeout): return
if DEBUG_PHASE: print 'initiated phase 3 = send to all'
target_save.extend(targets[:])
associate_all_ip_with_all_mac_addresses()
reset_state()
phases.pop(0)
if not targets:
give_up()
#phase
def do_scanner_phase():
# wait to timeout without exiting wait
# start broadcasting instead of using real mac address
if not state_has_not_changed_for(timeout): return
if DEBUG_PHASE: print 'initiated phase 4 = broadcast'
if add_broadcast_to_all_mac_addresses():
with targets_lock:
targets.extend(target_save)
reset_state()
give_up()
#phase
def do_scanner_phase():
# wait to timeout without exiting wait
# give up
if not state_has_not_changed_for(timeout): return
if DEBUG_PHASE: print 'initiated phase 5 = give up'
for device_name in scanning.keys():
scanning.pop(device_name)
reset_state()
phases.insert(0, phases.pop(-1))
#phase
def do_scanner_phase():
pass
def give_up():
phases.insert(0, phases.pop(-2))
def send_packets_to_addresses_to_sniff_mac():
udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for host in target_hosts:
send_udp(udp_sock, host)
try:
raw_sock = socket.socket(socket.AF_INET, socket.SOCK_RAW)
except:
sys.stderr.write('higher previleges needed to perform raw socket packet send\n')
return
for target in targets:
send_raw(raw_sock, target)
def send_raw(raw_sock, (family, addr)):
if family == socket.AF_INET:
send_raw_ipv4(raw_sock, addr)
elif family == socket.AF_INET6:
pass # todo: ipv6
else:
raise ValueError('invalid family %s' % (family,))
def send_raw_ipv4(raw_sock, addr):
for tcp in iter_tcp_packets(addr):
if DEBUG_PHASE2: print 'sending tcp raw', repr(tcp.get_packet()), addr
try:
raw_sock.sendto(tcp.get_packet(), addr)
except ():
pass
def send_udp(s, host):
# send an udp packet to sniff mac address
try:
s.sendto(':)', (host, random.randint(0, 0xffff)))
except socket_error as e:
if DEBUG_PHASE2: print 'failed: send to %r %s' % (host, e)
else:
if DEBUG_PHASE2: print 'succeded: send to %r' % (host,)
s.close()
def associate_all_ip_with_all_mac_addresses():
macs = set()
for mac in mac_addresses.values():
macs.update(mac)
for mac in mac_addresses.values():
mac.update(macs)
if DEBUG_PHASE: print 'macs:', [mac for mac in macs]
def add_broadcast_to_all_mac_addresses():
updated_mac = False
BC = ('B', ETHER_BROADCAST)
for mac in mac_addresses.values():
updated_mac = updated_mac or not BC in mac
mac.add(('B', ETHER_BROADCAST))
return updated_mac
def do_notify():
t = time.time()
notified = set()
for target in targets[:]:
ip = target[1][IP]
if ip in notified:
continue
if DEBUG_SYN:
print 'nofifying %s' % ip,
if ip_known(ip):
if DEBUG_SYN:print 'send_syn', target[PORT]
send_syn(*target)
targets.remove(target)
else:
if DEBUG_SYN:print 'notify'
notify(*target)
notified.add(ip)
t -= time.time() - NOTIFY_TIMEOUT
if t > 0:
time.sleep(t)
def start_notify_loop():
thread.start_new(notify_loop, ())
store_ip_mac_resolution_for(socket.gethostname())
start_scans()
start_notify_loop()
return obj(wait = wait, add_scan = add_scan)
def main():
host, ports, timeout = parseArgs(DEFAULT_SOCKET_TIMEOUT)
scanner = start_scan(timeout)
for connection in connections(host, ports):
scanner.add_scan(connection)
scanner.wait()
if __name__ == '__main__':
main()
I ran into similar problem. I guess when there is no documentation, the best documentation is the source code! And with python we are lucky to have source code most of the time. Anyway, I would suggest looking into ImpactDecoder.py and ImpactPacket.py. First one give some insights as far as how packets get decoded and second gives information on actual packets as a class and their methods. For instance, ImpactPacket.py and class PacketBuffer has following methods that you were probably looking for::
def set_bytes_from_string(self, data):
"Sets the value of the packet buffer from the string 'data'"
self.__bytes = array.array('B', data)
def get_buffer_as_string(self):
"Returns the packet buffer as a string object"
return self.__bytes.tostring()
def get_bytes(self):
"Returns the packet buffer as an array"
return self.__bytes
def set_bytes(self, bytes):
"Set the packet buffer from an array"
# Make a copy to be safe
self.__bytes = array.array('B', bytes.tolist())
def set_byte(self, index, value):
"Set byte at 'index' to 'value'"
index = self.__validate_index(index, 1)
self.__bytes[index] = value
def get_byte(self, index):
"Return byte at 'index'"
index = self.__validate_index(index, 1)
return self.__bytes[index]
def set_word(self, index, value, order = '!'):
"Set 2-byte word at 'index' to 'value'. See struct module's documentation to understand the meaning of 'order'."
index = self.__validate_index(index, 2)
ary = array.array("B", struct.pack(order + 'H', value))
if -2 == index:
self.__bytes[index:] = ary
else:
self.__bytes[index:index+2] = ary
def get_word(self, index, order = '!'):
"Return 2-byte word at 'index'. See struct module's documentation to understand the meaning of 'order'."
index = self.__validate_index(index, 2)
if -2 == index:
bytes = self.__bytes[index:]
else:
bytes = self.__bytes[index:index+2]
(value,) = struct.unpack(order + 'H', bytes.tostring())
return value
def set_long(self, index, value, order = '!'):
"Set 4-byte 'value' at 'index'. See struct module's documentation to understand the meaning of 'order'."
index = self.__validate_index(index, 4)
ary = array.array("B", struct.pack(order + 'L', value))
if -4 == index:
self.__bytes[index:] = ary
else:
self.__bytes[index:index+4] = ary
def get_long(self, index, order = '!'):
"Return 4-byte value at 'index'. See struct module's documentation to understand the meaning of 'order'."
index = self.__validate_index(index, 4)
if -4 == index:
bytes = self.__bytes[index:]
else:
bytes = self.__bytes[index:index+4]
(value,) = struct.unpack(order + 'L', bytes.tostring())
return value
def set_long_long(self, index, value, order = '!'):
"Set 8-byte 'value' at 'index'. See struct module's documentation to understand the meaning of 'order'."
index = self.__validate_index(index, 8)
ary = array.array("B", struct.pack(order + 'Q', value))
if -8 == index:
self.__bytes[index:] = ary
else:
self.__bytes[index:index+8] = ary
def get_long_long(self, index, order = '!'):
"Return 8-byte value at 'index'. See struct module's documentation to understand the meaning of 'order'."
index = self.__validate_index(index, 8)
if -8 == index:
bytes = self.__bytes[index:]
else:
bytes = self.__bytes[index:index+8]
(value,) = struct.unpack(order + 'Q', bytes.tostring())
return value
def get_ip_address(self, index):
"Return 4-byte value at 'index' as an IP string"
index = self.__validate_index(index, 4)
if -4 == index:
bytes = self.__bytes[index:]
else:
bytes = self.__bytes[index:index+4]
return socket.inet_ntoa(bytes.tostring())
def set_ip_address(self, index, ip_string):
"Set 4-byte value at 'index' from 'ip_string'"
index = self.__validate_index(index, 4)
raw = socket.inet_aton(ip_string)
(b1,b2,b3,b4) = struct.unpack("BBBB", raw)
self.set_byte(index, b1)
self.set_byte(index + 1, b2)
self.set_byte(index + 2, b3)
self.set_byte(index + 3, b4)
The other super useful class from ImpactPacket.py is ProtocolLayer, that gives us following methods::
def child(self):
"Return the child of this protocol layer"
return self.__child
def parent(self):
"Return the parent of this protocol layer"
return self.__parent
So, basically impacket uses matreshka doll approach, and you can go to any layer you want using child and parent methods and use any methods of the PacketBuffer class on any layer. Pretty cool, huh? Furthermore, particular layers (or packets) have their specific methods but you would have to go dig ImpactPacket.py and ImpactDecoder.py if you want to find more about those.
Good luck and cheers mate!
Here is a sample code written in Python with working pcapy. This might be of help for many.
'''
Packet sniffer in python using the pcapy python library
Project website
http://oss.coresecurity.com/projects/pcapy.html
'''
import socket
from struct import *
import datetime
import pcapy
import sys
import socket
def main(argv):
#list all devices
devices = pcapy.findalldevs()
print devices
errbuf = ""
#ask user to enter device name to sniff
print "Available devices are :"
for d in devices :
print d
dev = raw_input("Enter device name to sniff : ")
print "Sniffing device " + dev
'''
open device
# Arguments here are:
# device
# snaplen (maximum number of bytes to capture _per_packet_)
# promiscious mode (1 for true)
# timeout (in milliseconds)
'''
socket.setdefaulttimeout(2)
s = socket.socket();
#s.settimeout(100);
#dev = 'eth0'
cap = pcapy.open_live(dev , 65536 , 1 , 1000)
#start sniffing packets
while(1) :
(header, packet) = cap.next()
#print ('%s: captured %d bytes, truncated to %d bytes' %(datetime.datetime.now(), header.getlen(), header.getcaplen()))
parse_packet(packet)
#start sniffing packets
#while(1) :
#print ('%s: captured %d bytes, truncated to %d bytes' %(datetime.datetime.now(), header.getlen(), header.getcaplen()))
#Convert a string of 6 characters of ethernet address into a dash separated hex string
def eth_addr (a) :
b = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x" % (ord(a[0]) , ord(a[1]) , ord(a[2]), ord(a[3]), ord(a[4]) , ord(a[5]))
return b
#function to parse a packet
def parse_packet(packet) :
#parse ethernet header
eth_length = 14
eth_header = packet[:eth_length]
eth = unpack('!6s6sH' , eth_header)
eth_protocol = socket.ntohs(eth[2])
print 'Destination MAC : ' + eth_addr(packet[0:6]) + ' Source MAC : ' + eth_addr(packet[6:12]) + ' Protocol : ' + str(eth_protocol)
#Parse IP packets, IP Protocol number = 8
if eth_protocol == 8 :
#Parse IP header
#take first 20 characters for the ip header
ip_header = packet[eth_length:20+eth_length]
#now unpack them :)
iph = unpack('!BBHHHBBH4s4s' , ip_header)
version_ihl = iph[0]
version = version_ihl >> 4
ihl = version_ihl & 0xF
iph_length = ihl * 4
ttl = iph[5]
protocol = iph[6]
s_addr = socket.inet_ntoa(iph[8]);
d_addr = socket.inet_ntoa(iph[9]);
print 'Version : ' + str(version) + ' IP Header Length : ' + str(ihl) + ' TTL : ' + str(ttl) + ' Protocol : ' + str(protocol) + ' Source Address : ' + str(s_addr) + ' Destination Address : ' + str(d_addr)
#TCP protocol
if protocol == 6 :
t = iph_length + eth_length
tcp_header = packet[t:t+20]
#now unpack them :)
tcph = unpack('!HHLLBBHHH' , tcp_header)
source_port = tcph[0]
dest_port = tcph[1]
sequence = tcph[2]
acknowledgement = tcph[3]
doff_reserved = tcph[4]
tcph_length = doff_reserved >> 4
print 'Source Port : ' + str(source_port) + ' Dest Port : ' + str(dest_port) + ' Sequence Number : ' + str(sequence) + ' Acknowledgement : ' + str(acknowledgement) + ' TCP header length : ' + str(tcph_length)
h_size = eth_length + iph_length + tcph_length * 4
data_size = len(packet) - h_size
#get data from the packet
data = packet[h_size:]
#print 'Data : ' + data
#ICMP Packets
elif protocol == 1 :
u = iph_length + eth_length
icmph_length = 4
icmp_header = packet[u:u+4]
#now unpack them :)
icmph = unpack('!BBH' , icmp_header)
icmp_type = icmph[0]
code = icmph[1]
checksum = icmph[2]
print 'Type : ' + str(icmp_type) + ' Code : ' + str(code) + ' Checksum : ' + str(checksum)
h_size = eth_length + iph_length + icmph_length
data_size = len(packet) - h_size
#get data from the packet
data = packet[h_size:]
#print 'Data : ' + data
#UDP packets
elif protocol == 17 :
u = iph_length + eth_length
udph_length = 8
udp_header = packet[u:u+8]
#now unpack them :)
udph = unpack('!HHHH' , udp_header)
source_port = udph[0]
dest_port = udph[1]
length = udph[2]
checksum = udph[3]
print 'Source Port : ' + str(source_port) + ' Dest Port : ' + str(dest_port) + ' Length : ' + str(length) + ' Checksum : ' + str(checksum)
h_size = eth_length + iph_length + udph_length
data_size = len(packet) - h_size
#get data from the packet
data = packet[h_size:]
#print 'Data : ' + data
#some other IP packet like IGMP
else :
print 'Protocol other than TCP/UDP/ICMP'
print
if __name__ == "__main__":
main(sys.argv)