├── README.md ├── micropython-v1.12-256-geae495a71-dirty_wifi_sniffer_injection_esp32-20200426.bin ├── micropython-v1.12-256-geae495a71-dirty_wifi_sniffer_injection_esp32-20200630.bin ├── micropython_sniffer_injection_example.py ├── mp_wifi_packet_per_second.py ├── wifi.py └── wifi_sniffer_example.py /README.md: -------------------------------------------------------------------------------- 1 | # esp32-micropython-wifi-sniff-inject 2 | esp32 build of micropython v1.12 using ESP-IDF v4.0 that supports 802.11 sniffing and injection. 3 | 4 | MicroPython v1.12.0 for ESP32 with 802.11 packet sniffing and injection. 5 | 6 | Firmware: micropython-v1.12-256-geae495a71-dirty_wifi_sniffer_injection_esp32-20200630.bin 7 | 8 | MicroPython: v1.12-256-geae495a71-dirty 9 | 10 | ESP-IDF: v4.0 11 | 12 | INSTRUCTIONS 13 | 14 | Flash this micropython firmware to your ESP32. 15 | 16 | ./esptool.py --chip esp32 --port /dev/cu.SLAB_USBtoUART --baud 460800 write_flash -z 0x1000 micropython-v1.12-256-geae495a71-dirty_wifi_sniffer_injection_esp32-20200630.bin 17 | 18 | Examples of how to use it are in micropython_sniffer_injection_example.py. 19 | 20 | 21 | -------------------------------------------------------------------------------- /micropython-v1.12-256-geae495a71-dirty_wifi_sniffer_injection_esp32-20200426.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicheSecTech/esp32-micropython-wifi-sniff-inject/15e4646e879f22dc644a9b11e75873cfc2f61e08/micropython-v1.12-256-geae495a71-dirty_wifi_sniffer_injection_esp32-20200426.bin -------------------------------------------------------------------------------- /micropython-v1.12-256-geae495a71-dirty_wifi_sniffer_injection_esp32-20200630.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NicheSecTech/esp32-micropython-wifi-sniff-inject/15e4646e879f22dc644a9b11e75873cfc2f61e08/micropython-v1.12-256-geae495a71-dirty_wifi_sniffer_injection_esp32-20200630.bin -------------------------------------------------------------------------------- /micropython_sniffer_injection_example.py: -------------------------------------------------------------------------------- 1 | import machine 2 | import _thread 3 | import socket 4 | import time 5 | 6 | machine.freq(240000000) 7 | 8 | ## DEFINE FUNCTION TO RECEIVE SNIFFED PACKETS FROM NETWORK SOCKET 9 | def wifi_sniff(): 10 | localIP = "127.0.0.1" 11 | localPort = 20001 12 | bufferSize = 4096 13 | UDPServerSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 14 | UDPServerSocket.bind((localIP, localPort)) 15 | print("UDP server up and listening for WiFi packets.") 16 | count = 0 17 | countme = 0 18 | while(True): 19 | count = count + 1 20 | bytesAddressPair = UDPServerSocket.recvfrom(bufferSize) 21 | # bytesAddressPair[0] is the data 22 | # bytesAddressPair[1] is the remote (ip,port) ('127.0.0.1',20002). Ignore this. 23 | print(bytesAddressPair) 24 | countme = countme + len(bytesAddressPair[0]) 25 | if count >= 100: 26 | print("Server: %d %d" % (time.ticks_ms(),countme)) 27 | count = 0 28 | 29 | ## START SNIFFING THREAD BEFORE CALLING sta.sniffer(ch=8) 30 | _thread.start_new_thread(wifi_sniff, ()) 31 | 32 | ## TURN ON THE WIFI 33 | import network 34 | ap = network.WLAN(network.AP_IF) 35 | ap.active(False) 36 | sta = network.WLAN(network.STA_IF) 37 | sta.active(False) 38 | ## START SNIFFER 39 | sta.sniffer(ch=8) 40 | ## STOP SNIFFER 41 | sta.sniffer_stop() 42 | 43 | ## Configure your ESP32 with AP mode or set the channel with sniffer 44 | ## first before doing injection. 45 | sta.sniffer(ch=8) 46 | sta.sniffer_stop() 47 | 48 | ## PACKET INJECTION (BEACON 'ESPTEST' ON CHANNEL 8) 49 | buf = b"\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\xba\xde\xaf\xfe\x00\x06\xba\xde\xaf\xfe\x00\x06\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x64\x00\x31\x04\x00\x07\x45\x53\x50\x54\x45\x53\x54\x01\x08\x82\x84\x8b\x96\x0c\x12\x18\x24\x03\x01\x08\x05\x04\x01\x02\x00\x00" 50 | buf_len = len(buf) 51 | count = 0 52 | while (count < 100): 53 | count = count + 1 54 | sta.inject(buffer=buf,length=buf_len) 55 | time.sleep(0.1) 56 | 57 | 58 | -------------------------------------------------------------------------------- /mp_wifi_packet_per_second.py: -------------------------------------------------------------------------------- 1 | 2 | ## Make sure you have uploaded wifi.py to the ESP32. 3 | ## Usage: 4 | ## >>> import mp_wifi_packet_per_second 5 | ## Output will be: 6 | ## 7 | ## Example output: 8 | # 1 50 9 | # 2 21 10 | # 3 12 11 | # 4 27 12 | # 5 15 13 | # 6 44 14 | # 7 0 15 | # 8 1 16 | # 9 15 17 | # 10 0 18 | # 11 26 19 | # 12 0 20 | # 13 0 21 | # 14 0 22 | 23 | import machine 24 | machine.freq(240000000) 25 | import network 26 | import _thread 27 | import socket 28 | import uselect 29 | import time 30 | import wifi 31 | from wifi import * 32 | 33 | @micropython.viper 34 | def monitorWiFi(): 35 | sta = network.WLAN(network.STA_IF) 36 | sta.active(False) 37 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 38 | sock.bind(("127.0.0.1", 20001)) 39 | packet = wifi.packet() 40 | count = 0 41 | channel = 1 42 | lasttime = time.ticks_ms() 43 | print("Starting sniffer....") 44 | sta.sniffer(ch=channel) 45 | in_socks = [sock] 46 | while (True): 47 | tt = time.ticks_ms() 48 | ts = int(tt) - int(lasttime) 49 | if int(ts) >= int(1000): 50 | print(channel,count) 51 | channel = ((channel + 1) % 15) 52 | if channel == 0: 53 | channel = 1 54 | sta.set_channel(channel) 55 | count = 0 56 | lasttime = time.ticks_ms() 57 | inputready, outputready, exceptready = uselect.select(in_socks, [], [], 1) 58 | for temp in inputready: 59 | count = count + 1 60 | temp_packet = temp.recv(4096) 61 | # packet.update(temp_packet) 62 | # packet.print_summary() 63 | 64 | print("Starting thread...") 65 | _thread.start_new_thread(monitorWiFi, ()) 66 | 67 | -------------------------------------------------------------------------------- /wifi.py: -------------------------------------------------------------------------------- 1 | import ubinascii 2 | import network 3 | 4 | 5 | ## USAGE 6 | # >>> import wifi 7 | 8 | ## Define a new packet class 9 | # >>> new_packet = wifi.packet() 10 | ## Updates the packet data (structure defined below) 11 | # >>> new_packet.update(raw_packet) 12 | 13 | ## PACKET TYPES 14 | # https://en.wikipedia.org/wiki/802.11_Frame_Types 15 | 16 | WIFI_TYPES = ["MANAGEMENT","CONTROL","DATA","EXTENSION"] 17 | WIFI_SUBTYPES = [["ASSOCIATION_REQUEST","ASSOCIATION_RESPONSE","REASSOCIATION_REQUEST","REASSOCIATION_RESPONSE","PROBE_REQUEST","PROBE_RESPONSE","TIMING_ADVERTISEMENT","RESERVED1","BEACON","ATIM","DISASSOCIATION","AUTHENTICATION","DEAUTHENTICATION","ACTION","NACK","RESERVED2"],["RESERVED1","RESERVED2","TRIGGER","RESERVED3","REPORT_POLL","NDP_ANNOUNCEMENT","CONTROL_FRAME_EXTENSION","CONTROL_WRAPPER","BLOCK_ACK_REQUEST","BLOCK_ACK","PS_POLL","RTS","CTS","ACK","CF_END","CF_END_ACK"],["DATA","DATA_CF_ACK","DATA_CF_POLL","DATA_CF_ACK_POLL","NULL","CF_ACK","CF_POLL","CF_ACK_POLL","QOS_DATA","QOS_DATA_CF_ACK","QOS_DATA_CF_POLL","QOS_DATA_CF_ACK_POLL","QOS_NULL","QOS_RESERVED1","QOS_CF_POLL","QOS_CF_ACK_POLL"],["DMG_BEACON"]] 18 | 19 | TYPE_MANAGEMENT = const(0) 20 | TYPE_MANAGEMENT_ASSOCIATION_REQUEST = const(0) 21 | TYPE_MANAGEMENT_ASSOCIATION_RESPONSE = const(1) 22 | TYPE_MANAGEMENT_REASSOCIATION_REQUEST = const(2) 23 | TYPE_MANAGEMENT_REASSOCIATION_RESPONSE = const(3) 24 | TYPE_MANAGEMENT_PROBE_REQUEST = const(4) 25 | TYPE_MANAGEMENT_PROBE_RESPONSE = const(5) 26 | TYPE_MANAGEMENT_TIMING_ADVERTISEMENT = const(6) 27 | TYPE_MANAGEMENT_RESERVED1 = const(7) 28 | TYPE_MANAGEMENT_BEACON = const(8) 29 | TYPE_MANAGEMENT_ATIM = const(9) 30 | TYPE_MANAGEMENT_DISASSOCIATION = const(10) 31 | TYPE_MANAGEMENT_AUTHENTICATION = const(11) 32 | TYPE_MANAGEMENT_DEAUTHENTICATION = const(12) 33 | TYPE_MANAGEMENT_ACTION = const(13) 34 | TYPE_MANAGEMENT_NACK = const(14) 35 | TYPE_MANAGEMENT_RESERVED2 = const(15) 36 | 37 | TYPE_CONTROL = const(1) 38 | TYPE_CONTROL_RESERVED1 = const(0) 39 | TYPE_CONTROL_RESERVED2 = const(1) 40 | TYPE_CONTROL_TRIGGER = const(2) 41 | TYPE_CONTROL_RESERVED3 = const(3) 42 | TYPE_CONTROL_REPORT_POLL = const(4) 43 | TYPE_CONTROL_NDP_ANNOUNCEMENT = const(5) 44 | TYPE_CONTROL_CONTROL_FRAME_EXTENSION = const(6) 45 | TYPE_CONTROL_CONTROL_WRAPPER = const(7) 46 | TYPE_CONTROL_BLOCK_ACK_REQUEST = const(8) #(BAR) 47 | TYPE_CONTROL_BLOCK_ACK = const(9) #(BA) 48 | TYPE_CONTROL_PS_POLL = const(10) 49 | TYPE_CONTROL_RTS = const(11) 50 | TYPE_CONTROL_CTS = const(12) 51 | TYPE_CONTROL_ACK = const(13) 52 | TYPE_CONTROL_CF_END = const(14) 53 | TYPE_CONTROL_CF_END_ACK = const(15) 54 | 55 | TYPE_DATA = const(2) 56 | TYPE_DATA_DATA = const(0) 57 | TYPE_DATA_DATA_CF_ACK = const(1) 58 | TYPE_DATA_DATA_CF_POLL = const(2) 59 | TYPE_DATA_DATA_CF_ACK_POLL = const(3) 60 | TYPE_DATA_NULL = const(4) # (no data) 61 | TYPE_DATA_CF_ACK = const(5) # (no data) 62 | TYPE_DATA_CF_POLL = const(6) # (no data) 63 | TYPE_DATA_CF_ACK_POLL = const(7) # (no data) 64 | TYPE_DATA_QOS_DATA = const(8) 65 | TYPE_DATA_QOS_DATA_CF_ACK = const(9) 66 | TYPE_DATA_QOS_DATA_CF_POLL = const(10) 67 | TYPE_DATA_QOS_DATA_CF_ACK_POLL = const(11) 68 | TYPE_DATA_QOS_NULL = const(12) # (no data) 69 | TYPE_DATA_QOS_RESERVED1 = const(13) # Reserved 70 | TYPE_DATA_QOS_CF_POLL = const(14) # (no data) 71 | TYPE_DATA_QOS_CF_ACK_POLL = const(15) # (no data) 72 | 73 | TYPE_EXTENSION = const(3) 74 | TYPE_EXTENSION_DMG_BEACON = const(0) 75 | 76 | 77 | def raw_to_hex(mac): 78 | output = ubinascii.hexlify(mac,":") 79 | return output 80 | 81 | def hex_to_raw(mac): 82 | new_mac = mac.replace(":","") 83 | output = ubinascii.unhexlify(new_mac) 84 | return output 85 | 86 | @micropython.native 87 | def get_my_mac(): 88 | mac = ubinascii.hexlify(network.WLAN().config('mac'),':').decode() 89 | return mac 90 | 91 | class packet: 92 | @micropython.native 93 | def __init__(self,packet=None): 94 | # FRAME CONTROL 95 | self.size = None 96 | self.fc_ver = None 97 | self.fc_type = None 98 | self.fc_sub = None 99 | self.fc_tods = None 100 | self.fc_fromds = None 101 | self.fc_frag = None 102 | self.fc_retry = None 103 | self.fc_pwmg = None 104 | self.fc_moredata = None 105 | self.fc_wep = None 106 | self.fc_order = None 107 | self.header = None 108 | # HEADER 109 | self.duration = None 110 | self.addr1 = None 111 | self.addr2 = None 112 | self.addr3 = None 113 | self.seq = None 114 | self.addr4 = None 115 | self.body = None 116 | self.crc = None 117 | if (packet != None): 118 | self.update(packet) 119 | 120 | @micropython.native 121 | def decode_tags(self): 122 | tags = self.body 123 | tags_dict = {} 124 | pos = 12 125 | pos_len = len(tags) 126 | dtags = bytes(bytearray(tags)) 127 | while pos < pos_len: 128 | tag_number = int(dtags[pos]) 129 | tag_length = int(dtags[(pos+1)]) 130 | tag_data = dtags[(pos+2):(pos+2+tag_length)] 131 | pos = pos + 2 + tag_length 132 | if tag_number not in tags_dict.keys(): 133 | tags_dict[tag_number] = [] 134 | tags_dict[tag_number].append(tag_data) 135 | return tags_dict 136 | 137 | @micropython.native 138 | def update(self,packet): 139 | # FRAME CONTROL 140 | self.size = len(packet) 141 | self.fc_ver = (((packet[0]) & 0b00000011) >> 0) 142 | self.fc_type = (((packet[0]) & 0b00001100) >> 2) 143 | self.fc_sub = (((packet[0]) & 0b11110000) >> 4) 144 | self.fc_tods = (((packet[1]) & 0b10000000) >> 7) 145 | self.fc_fromds = (((packet[1]) & 0b01000000) >> 6) 146 | self.fc_frag = (((packet[1]) & 0b00100000) >> 5) 147 | self.fc_retry = (((packet[1]) & 0b00010000) >> 4) 148 | self.fc_pwmg = (((packet[1]) & 0b00001000) >> 3) 149 | self.fc_more = (((packet[1]) & 0b00000100) >> 2) 150 | self.fc_wep = (((packet[1]) & 0b00000010) >> 1) 151 | self.fc_order = (((packet[1]) & 0b00000001) >> 0) 152 | 153 | # HEADER 154 | self.duration = packet[2:3] 155 | self.addr1 = packet[4:10] 156 | self.addr2 = packet[10:16] 157 | self.addr3 = packet[16:22] 158 | self.seq = packet[22:23] 159 | # self.addr4 = packet[24:30] 160 | self.body = packet[24:-4] 161 | self.crc = packet[-4:] 162 | 163 | def print_summary(self): 164 | try: 165 | ptype = WIFI_TYPES[int(self.fc_type)] 166 | except: 167 | ptype = int(self.fc_type) 168 | try: 169 | stype = WIFI_SUBTYPES[int(self.fc_type)][int(self.fc_sub)] 170 | except: 171 | stype = int(self.fc_sub) 172 | print("%d %s %s %s %s %s" % (self.size,ptype,stype,raw_to_hex(self.addr1),raw_to_hex(self.addr2),raw_to_hex(self.addr3))) 173 | 174 | def print_packet(self): 175 | print("self.fc_ver: %d" % self.fc_ver) 176 | print("self.fc_type: %d" % self.fc_type) 177 | print("self.fc_sub: %d" % self.fc_sub) 178 | print("self.fc_tods: %d" % self.fc_tods) 179 | print("self.fc_fromds: %d" % self.fc_fromds) 180 | print("self.fc_frag: %d" % self.fc_frag) 181 | print("self.fc_retry: %d" % self.fc_retry) 182 | print("self.fc_pwmg: %d" % self.fc_pwmg) 183 | print("self.fc_more: %d" % self.fc_more) 184 | print("self.fc_wep: %d" % self.fc_wep) 185 | print("self.fc_order: %d" % self.fc_order) 186 | print("self.duration: %s" % raw_to_hex(self.duration)) 187 | print("self.addr1: %s" % raw_to_hex(self.addr1)) 188 | print("self.addr2: %s" % raw_to_hex(self.addr2)) 189 | print("self.addr3: %s" % raw_to_hex(self.addr3)) 190 | print("self.seq: %s" % raw_to_hex(self.seq)) 191 | # print("self.addr4: %s" % raw_to_hex(self.addr4)) 192 | print("self.body(hex): %s" % raw_to_hex(self.body)) 193 | print("self.crc: %s" % raw_to_hex(self.crc)) 194 | -------------------------------------------------------------------------------- /wifi_sniffer_example.py: -------------------------------------------------------------------------------- 1 | import machine 2 | machine.freq(240000000) 3 | import _thread 4 | import network 5 | import usocket as socket 6 | import utime as time 7 | import wifi 8 | 9 | channel = 1 10 | 11 | @micropython.native 12 | def read_wifi_packets(): 13 | global channel 14 | beacon_list = [] 15 | ### Listen on UDP port 20001 for wifi packets from the lwIP network stack on core 0. 16 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 17 | sock.bind(("127.0.0.1", 20001)) 18 | ### Use the packet class 19 | packet = wifi.packet() 20 | while (True): 21 | try: 22 | ### Read the wifi packet from the UDP socket. 23 | temp_packet = sock.recv(4096) 24 | ### Optimization 25 | mv = memoryview(temp_packet) 26 | ### Update the packet 27 | packet.update(mv) 28 | 29 | ### Print detailed packet info (EXTRA VERBOSE) 30 | # packet.print_packet() 31 | 32 | # ### Example to print PROBE REQUESTS 33 | if (packet.fc_type == wifi.TYPE_MANAGEMENT) and (packet.fc_sub == wifi.TYPE_MANAGEMENT_PROBE_REQUEST): 34 | tags = packet.decode_tags() 35 | packet_src = wifi.raw_to_hex(packet.addr2).decode('utf-8') 36 | packet_dst = wifi.raw_to_hex(packet.addr1).decode('utf-8') 37 | packet_bssid = wifi.raw_to_hex(packet.addr3).decode('utf-8') 38 | try: 39 | # probe_len = int(packet.body[1]) + 2 40 | # probe_ssid = bytes(packet.body[2:probe_len]).decode('utf-8') 41 | probe_ssid = tags[0][0].decode('utf-8') 42 | print("[%d] PROBE REQUEST: %s > %s %s '%s'" % (channel,packet_src,packet_dst,packet_bssid,probe_ssid)) 43 | except: 44 | pass 45 | 46 | # ### Example to print PROBE RESPONSE 47 | if (packet.fc_type == wifi.TYPE_MANAGEMENT) and (packet.fc_sub == wifi.TYPE_MANAGEMENT_PROBE_RESPONSE): 48 | packet_src = wifi.raw_to_hex(packet.addr2).decode('utf-8') 49 | packet_dst = wifi.raw_to_hex(packet.addr1).decode('utf-8') 50 | packet_bssid = wifi.raw_to_hex(packet.addr3).decode('utf-8') 51 | try: 52 | # probe_len = int(packet.body[1]) + 2 53 | probe_ssid = tags[0][0].decode('utf-8') 54 | # probe_ssid = bytes(packet.body[2:probe_len]).decode('utf-8') 55 | print("[%d] PROBE RESPONSE: %s > %s %s '%s'" % (channel,packet_src,packet_dst,packet_bssid,probe_ssid)) 56 | except: 57 | pass 58 | 59 | # ### Example to print BEACONS 60 | if (packet.fc_type == wifi.TYPE_MANAGEMENT) and (packet.fc_sub == wifi.TYPE_MANAGEMENT_BEACON): 61 | tags = packet.decode_tags() 62 | essid = tags[0][0].decode('utf-8') 63 | beacon_chan = ord(tags[3][0].decode('utf-8')) 64 | packet_src = wifi.raw_to_hex(packet.addr2).decode('utf-8') 65 | packet_dst = wifi.raw_to_hex(packet.addr1).decode('utf-8') 66 | packet_bssid = wifi.raw_to_hex(packet.addr3).decode('utf-8') 67 | check_entry = (beacon_chan,packet_src,essid) 68 | if check_entry not in beacon_list: 69 | beacon_list.append(check_entry) 70 | print("[%d] BEACON: %s '%s'" % (check_entry[0],check_entry[1],check_entry[2])) 71 | 72 | except Exception as error: 73 | print("ERROR %s\n%s" % (error,temp_packet)) 74 | 75 | 76 | 77 | print("Starting UDP listener thread to recieve wifi packets...") 78 | _thread.start_new_thread(read_wifi_packets, ()) 79 | 80 | print("Starting the sniffer....") 81 | sta = network.WLAN(network.STA_IF) 82 | sta.active(False) 83 | sta.sniffer(ch=channel) 84 | 85 | ## Uncomment below to do channel hopping 86 | while (True): 87 | for channel in range(1,12): 88 | sta.sniffer(ch=channel) 89 | time.sleep(3) 90 | --------------------------------------------------------------------------------