# Sniffer
###### tags: `python` `hacking` `sniffer` `Networking`
[TOC]
# Capture the singular packet
Access to the raw packet is different between Windows and Linux
## Under the Windows
We have to add some flags to header, if we are running system on windows.
if we want to know what OS we're using, we could use os.name.
os.name == 'nt' (nt means that we're running window. and 'posix'MAC)
### Setting Protocol
```python
def main ():
if os == 'nt':
socketProtocol = socket.IPPROTO_IP # Windows can listen all packets, it won't be constrained
else:
socketProtocol = socket.IPPROTO_ICMP #Linux only can listin ICMP packet
```
### Create the socket
```python
#Create the socket object
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socketProtocol)
sniffer.bind((host,0))
```
### include ip header
```python
#when we're capturing, include ip header
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
```
### Enable Promiscuous Model
```
if os.name =="nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
```
### Print packet
```python
print(sniffer.recvfrom(65565))
```
### Disable Promiscuous Model
```python
if os.name == 'nt':
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
```
## Source code
```python
import socket
import os
host = '10.0.0.182'
def main():
if os == 'nt':
socketProtocol = socket.IPPROTO_IP # Windows can listen all packets, it won't be constrained
else:
socketProtocol = socket.IPPROTO_ICMP #Linux only can listin ICMP packet
#Create the socket object
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socketProtocol)
sniffer.bind((host,0))
#when we're capturing, include ip header
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
if os.name =="nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
print(sniffer.recvfrom(65565))
if os.name == 'nt':
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
if __name__ == "__main__":
main()
```
## Result

---
# Parse IP Header and ICMP
We need to trans binary into the data structure.
And then, we have two model to defined data structure
model 1: ctype
modle 2: struct
## ctypes
### Library
```python
from ctypes import *
import socket
import struct
```
before we parse the ip header, we have to create a class to put header information
_fields_ structure is extend from the Structure class of Ctype.
Before we create an any object, we're going to define a _fields structure.
Just like this
```python
class IP(Structure):
_fields_ =[
("version", c_ubyte,4),
("headerLength", c_ubyte,4),
("typeOfServer", c_ubyte,8),
("totolLength", c_ushort,16),
("identification", c_ushort,16),
("offset",c_short,16),
("timeToLive", c_byte,8),
("protocol", c_ubyte,8),
("headerCheckSum",c_ushort,16)
("sourceIP", c_uint32,32),
("destinationIP",c_uint32,32)
]
```
Each field description use three parameter
- first: field name
- Second: a type of a vale
- three: bits length
c_ubyte means unsigned char
c_ushort means unsigned short
### New method
```python
def __new__(cls, socket_buffer=None):
return cls.from_buffer_copy(socket_buffer)
```
function:
padding _field_ structure
Generate an object
### Construct
```python
def __init__(self,socket_buffer=None):
self.srcAddress = socket.inet_ntoa(struct.pack("<L",self.sourceIP)) #inet_ntoa trans bytes into strings
self.dstAddress = socket.inet_ntoa(struct.pack("<L",self.destinationIP))
```
## Struct
### library
```python
import ipaddress
import struct
```
### IP class
```python
class IP:
def __init__(self, buff=None):
header = struct.unpack('<BBHHHBBH4s4s', buff) #little-endian
# Niabble (It's four-bits aggregation)
self.version = header[0] >> 4 #it means right-shift 4 bits (Hight singnificate)
self.headerLength =header[0] & 0xF # do and 00001111 (Low singnificate)
self.typeOfservice=header[1]
self.totalLength=header[2]
self.identification=header[3]
self.offset=header[4]
self.timeToLive=header[5]
self.protocol=header[6]
self.checkSum=header[7]
self.source=header[8]
self.destination=header[9]
self.srcAddressReadable = ipaddress.ip_address(self.source)
self.desAddressReadable = ipaddress.ip_address(self.destination)
```
< means the Endian
What is the Endian, it's very sample
**Memory layout**



In little endian machine, LSB is going to put first, on the other hand , MSB is goint to put first.
B 1byte (unsigned charter
H 2bytes (unsigned shorted number
4s 4-bytes (strings
### Nibble (4-bit data unit)
```python
self.version = header[0] >> 4 #it means right-shift 4 bits (Hight singnificate)
self.headerLength =header[0] & 0xF # do and 00001111 (Low singnificate)
```
#### Get version
We could use way is right shift 4 bit

#### Get Header length
### Source code
```python
import ipaddress
import socket
import struct
import sys
import os
class IPdecode:
def __init__(self, buff=None):
header = struct.unpack('<BBHHHBBH4s4s', buff) #little-endian
# Niabble (It's four-bits aggregation)
self.version = header[0] >> 4 #it means right-shift 4 bits (Hight singnificate)
self.headerLength =header[0] & 0xF # do and 00001111 (Low singnificate)
self.typeOfservice=header[1]
self.totalLength=header[2]
self.identification=header[3]
self.offset=header[4]
self.timeToLive=header[5]
self.protocolNumber=header[6]
self.checkSum=header[7]
self.source=header[8]
self.destination=header[9]
self.srcAddressReadable = ipaddress.ip_address(self.source)
self.desAddressReadable = ipaddress.ip_address(self.destination)
self.protocolMap ={
1:"ICMP",
6:"TCP",
17:"UDP"
}
try:
self.protocol = self.protocolMap[self.protocolNumber]
except Exception as e:
print(f"{e} NO protocol for {self.protocolNumber} ")
self.protocol = str(self.protocolNumber)
def sniff(targetHost):
if os.name =="nt":
socketProtocol = socket.IPPROTO_IP
else :
socketProtocol = socket.IPPROTO_ICMP
snifferSocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socketProtocol)
snifferSocket.bind((targetHost,0))
# Adding the ip header
snifferSocket.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
if os.name == "nt":
snifferSocket.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
try:
while 1:
#Read packets
rawBuffer = snifferSocket.recvfrom(65536)[0]
#create the ip header
IPHeader = IPdecode(rawBuffer[0:20])
print(f"{IPHeader.protocol} {IPHeader.srcAddressReadable}->{IPHeader.desAddressReadable}")
except KeyboardInterrupt :
if os.name == 'nt':
snifferSocket.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
sys.exit()
if __name__ == "__main__":
if len(sys.argv) == 2:
targetHost = sys.argv[1]
else:
targetHost = "192.168.151.54"
sniff(targetHost)
```


## Decode ICMP
sniffer_with_icmp.py

Library
```
import ipaddress
import os
import socket
import struct
import sys
```
ICMP
要去decode 部分
- type
- code
- checksum
可以去看 type,code的Value
e.g.
code = Port Unreachable
type = Destination Unreachable
下面URL 有更多 type,code info
https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-types
定義 ICMP Structure
```
class ICMP:
def __init__(self, buff):
header = struct.unpack('<BBHHH', buff)
self.type = header[0]
self.code = header[1]
self.sum = header[2]
self.id = header[3]
self.seq = header[4]
```
去認收到 ICMP packet
```
if ip_header.protocol == "ICMP":
print('Protocol: %s %s -> %s' % (ip_header.protocol, ip_header.src_address, ip_header.dst_address))
print(f'Version: {ip_header.ver} Header Length: {ip_header.ihl} TTL: {ip_header.ttl}')
```
Calculate ICMP offest
去取出 ICMP 的type 和 code
Internet Header Length
會給出 有幾個 4byte 數量
e.g. 5 * 4 = 20 (NO options)
5 -> 32 bit 的數量
ip Header 以32 bit 0x00000000
```
# calculate where our ICMP packet starts
offset = ip_header.ihl * 4
buf = raw_buffer[offset:offset + 8]
icmp_header = ICMP(buf)
print('ICMP -> Type: %s Code: %s\n' % (icmp_header.type, icmp_header.code))
```
Source Code
```python
import ipaddress
import os
import socket
import struct
import sys
class IP:
def __init__(self, buff=None):
header = struct.unpack('<BBHHHBBH4s4s', buff)
self.ver = header[0] >> 4
self.ihl = header[0] & 0xF
self.tos = header[1]
self.len = header[2]
self.id = header[3]
self.offset = header[4]
self.ttl = header[5]
self.protocol_num = header[6]
self.sum = header[7]
self.src = header[8]
self.dst = header[9]
# human readable IP addresses
self.src_address = ipaddress.ip_address(self.src)
self.dst_address = ipaddress.ip_address(self.dst)
# map protocol constants to their names
self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}
try:
self.protocol = self.protocol_map[self.protocol_num]
except Exception as e:
print('%s No protocol for %s' % (e, self.protocol_num))
self.protocol = str(self.protocol_num)
class ICMP:
def __init__(self, buff):
header = struct.unpack('<BBHHH', buff)
self.type = header[0]
self.code = header[1]
self.sum = header[2]
self.id = header[3]
self.seq = header[4]
def sniff(host):
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:
raw_buffer = sniffer.recvfrom(65535)[0]
ip_header = IP(raw_buffer[0:20])
if ip_header.protocol == "ICMP":
print('Protocol: %s %s -> %s' % (ip_header.protocol, ip_header.src_address, ip_header.dst_address))
print(f'Version: {ip_header.ver} Header Length: {ip_header.ihl} TTL: {ip_header.ttl}')
# calculate where our ICMP packet starts
offset = ip_header.ihl * 4
buf = raw_buffer[offset:offset + 8]
icmp_header = ICMP(buf)
print('ICMP -> Type: %s Code: %s\n' % (icmp_header.type, icmp_header.code))
except KeyboardInterrupt:
if os.name == 'nt':
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
sys.exit()
if __name__ == '__main__':
if len(sys.argv) == 2:
host = sys.argv[1]
else:
host = '192.168.1.206'
sniff(host)
```