树莓派实现Wi-Fi探针功能

2017/02/27 程序

树莓派是基于ARM架构的计算机开发板,运行Linux操作系统,大小只有信用卡这么大。树莓派集成了USB、以太网、显示输出、音频输出等常用的接口,只需要提供5V的供电和一张TF储存卡,就能够搭建起计算机系统。

Wi-Fi探针

终端设备在接入无线网络之前通过主动扫描或者被动扫描的方式识别周围的SSID,其中主动扫描的过程是终端设备广播一条Probe Resquest帧,对应的AP返回Probe Response帧,通过交换信息完成后续的连接工作。我们将这个Probe帧简单的理解为Wi-Fi探针。Probe帧里面包含一个很重要的信息,那就是终端的MAC地址,相当于是设备的唯一标识码。

一个常见的应用场景是,当终端设备启动WLAN功能,将会主动扫描周围环境中的无线网络,发送Probe Resquest帧。因为这个帧的目的地是广播地址,所以周围的AP都能够接收,此时AP可以从中解析出MAC地址,并且根据MAC地址的0~23位组织唯一标识符来得到设备的所属公司。通过收集此类信息,可以实现人流控制、考勤、客流统计等功能。

当周围的Wi-Fi探针设备部署越多,覆盖的范围越广,那么能够收集的数据量越大,从而获得更高的信息价值,例如设备的移动轨迹、停留时间、位置(通过RSSI计算)等等。

网卡开启监控模式

无线网卡一般具备多种工作模式,常见的有Master、Managed、WDS、Monitor。这里我们采取Monitor模式,无线网卡处于此模式下会将所有经过网卡的数据包都提交上层进行分析,不区分流过网卡的数据包的目的地是否为本机地址。基于这样的环境下我们可以对数据包进行拦截分析。

首先将无线网卡关闭

#ifconfig wlan0 down

开启monitor模式

#iwconfig wlan0 mode minitor

开启网卡

#ifconfig wlan0 up

Probemon

A simple command line tool for monitoring and logging 802.11 probe frames. I decided to build this simple python script using SCPAY so that I could record 802.11 probe frames over a long period of time. This was specifically useful in my use case: proving that a person or device was present at a given location at a given time.

#!/usr/bin/python

import time
import datetime
import argparse
import netaddr
import sys
import logging
from scapy.all import *
from pprint import pprint
from logging.handlers import RotatingFileHandler


NAME = 'probemon'
DESCRIPTION = "a command line tool for logging 802.11 probe request frames"

DEBUG = False

def build_packet_callback(time_fmt, logger, delimiter, mac_info, ssid, rssi):
	def packet_callback(packet):
		
		if not packet.haslayer(Dot11):
			return

		# we are looking for management frames with a probe subtype
		# if neither match we are done here
		if packet.type != 0 or packet.subtype != 0x04:
			return

		# list of output fields
		fields = []

		# determine preferred time format 
		log_time = str(int(time.time()))
		if time_fmt == 'iso':
			log_time = datetime.datetime.now().isoformat()

		fields.append(log_time)

		# append the mac address itself
		fields.append(packet.addr2)

		# parse mac address and look up the organization from the vendor octets
		if mac_info:
			try:
				parsed_mac = netaddr.EUI(packet.addr2)
				fields.append(parsed_mac.oui.registration().org)
			except netaddr.core.NotRegisteredError, e:
				fields.append('UNKNOWN')

		# include the SSID in the probe frame
		if ssid:
			fields.append(packet.info)
			
		if rssi:
			rssi_val = -(256-ord(packet.notdecoded[-4:-3]))
			fields.append(str(rssi_val))

		logger.info(delimiter.join(fields))

	return packet_callback

def main():
	parser = argparse.ArgumentParser(description=DESCRIPTION)
	parser.add_argument('-i', '--interface', help="capture interface")
	parser.add_argument('-t', '--time', default='iso', help="output time format (unix, iso)")
	parser.add_argument('-o', '--output', default='probemon.log', help="logging output location")
	parser.add_argument('-b', '--max-bytes', default=5000000, help="maximum log size in bytes before rotating")
	parser.add_argument('-c', '--max-backups', default=99999, help="maximum number of log files to keep")
	parser.add_argument('-d', '--delimiter', default='\t', help="output field delimiter")
	parser.add_argument('-f', '--mac-info', action='store_true', help="include MAC address manufacturer")
	parser.add_argument('-s', '--ssid', action='store_true', help="include probe SSID in output")
	parser.add_argument('-r', '--rssi', action='store_true', help="include rssi in output")
	parser.add_argument('-D', '--debug', action='store_true', help="enable debug output")
	parser.add_argument('-l', '--log', action='store_true', help="enable scrolling live view of the logfile")
	args = parser.parse_args()

	if not args.interface:
		print "error: capture interface not given, try --help"
		sys.exit(-1)
	
	DEBUG = args.debug

	# setup our rotating logger
	logger = logging.getLogger(NAME)
	logger.setLevel(logging.INFO)
	handler = RotatingFileHandler(args.output, maxBytes=args.max_bytes, backupCount=args.max_backups)
	logger.addHandler(handler)
	if args.log:
		logger.addHandler(logging.StreamHandler(sys.stdout))
	built_packet_cb = build_packet_callback(args.time, logger, 
		args.delimiter, args.mac_info, args.ssid, args.rssi)
	sniff(iface=args.interface, prn=built_packet_cb, store=0)

if __name__ == '__main__':
	main()

Search

    Table of Contents