December 5, 2022
Decoding ip address with python | for hackers

Decoding ip address with python | for hackers

What Is IP Layer

The internet layer is a group of internetworking methods, protocols, and specifications in the Internet protocol suite that are used to transport network packets from the originating host across network boundaries; if necessary, to the destination host specified by an IP address. The internet layer derives its name from its function facilitating internetworking, which is the concept of connecting multiple networks with each other through gateways.

Decoding The IP Layer

In its current form, our sniffer receives all of the IP headers along with any higher protocols such as TCP, UDP, or ICMP. The information is packed into binary form, and as shown above, is quite difficult to under- stand. We are now going to work on decoding the IP portion of a packet so that we can pull useful information out such as the protocol type (TCP, UDP, ICMP), and the source and destination IP addresses. This will be the foundation for you to start creating further protocol parsing later on.

If we examine what an actual packet looks like on the network, you will have an understanding of how we need to decode the incoming packets. Refer to Figure 3-1 for the makeup of an IP header.

We will decode the entire IP header (except the Options field) and extract the protocol type, source, and destination IP address. Using the Python ctypes module to create a C-like structure will allow us to have a friendly format for handling the IP header and its member fields. First, let’s take a look at the C definition of what an IP header looks like.

struct ip {
 u_char ip_hl:4;
 u_char ip_v:4;
 u_char ip_tos;
 u_short ip_len;
 u_short ip_id;
 u_short ip_off;
 u_char ip_ttl;
 u_char ip_p;
 u_short ip_sum;
 u_long ip_src;
 u_long ip_dst;
}

You now have an idea of how to map the C data types to the IP header values. Using C code as a reference when translating to Python objects can be useful because it makes it seamless to convert them to pure Python. Of note, the ip_hl and ip_v fields have a bit notation added to them (the :4 part). This indicates that these are bit fields, and they are 4 bits wide. We will use a pure Python solution to make sure these fields map correctly so we can avoid having to do any bit manipulation. Let’s implement our IP decoding routine into sniffer_ip_header_decode.py as shown below.

import socket
import os
import struct
from ctypes import *
# host to listen on
host = "192.168.0.187"
# our IP header
 class IP(Structure): 
 _fields_ = [
 ("ihl", c_ubyte, 4),
 ("version", c_ubyte, 4),
 ("tos", c_ubyte),
 ("len", c_ushort),
 ("id", c_ushort),
 ("offset", c_ushort),
 ("ttl", c_ubyte),
 ("protocol_num", c_ubyte),
 ("sum", c_ushort),
 ("src", c_ulong),
 ("dst", c_ulong)
 ]
 def __new__(self, socket_buffer=None):
 return self.from_buffer_copy(socket_buffer) 
 def __init__(self, socket_buffer=None):
 # map protocol constants to their names
 self.protocol_map = {1:"ICMP", 6:"TCP", 17:"UDP"}

The first step is defining a Python ctypes structure u that will map the first 20 bytes of the received buffer into a friendly IP header. As you can see, all of the fields that we identified and the preceding C structure match up nicely. The __new__ method of the IP class simply takes in a raw buffer (in this case, what we receive on the network) and forms the structure from it.

# human readable IP addresses 
 self.src_address = socket.inet_ntoa(struct.pack("<L",self.src)) 
 self.dst_address = socket.inet_ntoa(struct.pack("<L",self.dst))
 # human readable protocol
 try:
 self.protocol = self.protocol_map[self.protocol_num]
 except:
 self.protocol = str(self.protocol_num)
# this should look familiar from the previous example
if os.name == "nt":
 socket_protocol = socket.IPPROTO_IP 
else:
 socket_protocol = socket.IPPROTO_ICMP
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
if os.name == "nt":
 sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
try:
 
 while True:

When the __init__ method is called, __new__ is already finished process- ing the buffer. Inside __init__, we are simply doing some housekeeping to give some human readable output for the protocol in use and the IP addresses.

# read in a packet
 raw_buffer = sniffer.recvfrom(65565)[0]

With our freshly minted IP structure, we now put in the logic to continually read in packets and parse their information. The first step is to read in the packet.

# create an IP header from the first 20 bytes of the buffer
 ip_header = IP(raw_buffer[0:20])

Now, We will pass the first 20 bytes to initialize our IP structure.

# print out the protocol that was detected and the hosts
 print "Protocol: %s %s -> %s" % (ip_header.protocol, ip_header.src_¬ 
address, ip_header.dst_address) 
# handle CTRL-C
except KeyboardInterrupt:
 # if we're using Windows, turn off promiscuous mode
 if os.name == "nt":
 sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

Next, we simply print out the information that we have captured.

Now Let’s Check Our Code

Let’s test out our previous code to see what kind of information we are extracting from the raw packets being sent. I definitely recommend that you do this test from your Windows machine, as you will be able to see TCP, UDP, and ICMP, which allows you to do some pretty neat testing (open up a browser, for example). If you are confined to Linux, then perform the previous ping test to see it in action. Open a terminal and type:

python sniffer_ip_header_decode.py

Now, because Windows is pretty chatty, you’re likely to see output immediately. I tested this script by opening Internet Explorer and going to www .google.com, and here is the output from our script:

Protocol: UDP 192.168.0.190 -> 192.168.0.1
Protocol: UDP 192.168.0.1 -> 192.168.0.190
Protocol: UDP 192.168.0.190 -> 192.168.0.187
Protocol: TCP 192.168.0.187 -> 74.125.225.183
Protocol: TCP 192.168.0.187 -> 74.125.225.183
Protocol: TCP 74.125.225.183 -> 192.168.0.187
Protocol: TCP 192.168.0.187 -> 74.125.225.183

Because we aren’t doing any deep inspection on these packets, we can only guess what this stream is indicating. My guess is that the first couple of UDP packets are the DNS queries to determine where google.com lives, and the subsequent TCP sessions are my machine actually connecting and downloading content from their web server.

To perform the same test on Linux, we can ping google.com, and the results will look something like this:

Protocol: ICMP 74.125.226.78 -> 192.168.0.190
Protocol: ICMP 74.125.226.78 -> 192.168.0.190
Protocol: ICMP 74.125.226.78 -> 192.168.0.190

You can already see the limitation: we are only seeing the response and only for the ICMP protocol. But because we are purposefully building a host discovery scanner, this is completely acceptable. We will now apply the same techniques we used to decode the IP header to decode the ICMP messages in the next article.

Also Check Packet Sniffer On Windows And Linux Using Python | For Hackers.

1 thought on “Decoding The IP Layer With Python

Leave a Reply

Your email address will not be published. Required fields are marked *