openwrt 中 WiFi探针的三种实现办法

WiFi探针的开发过程在我之前的那个项目大致分了四段吧,就是用到了四种方法。

第一个是用 PCAP 库进行抓包,获取到数据,再进行处理,可惜我用的时候运行起来了,但是没得到数据,可能是芯片不支持吧。

第二个是在固件中做功夫,修改 802.11 的无线驱动,再通过 netlink 从内核中把数据传递到应用层进行处理,这个方法比前一个简单,但是很可惜,我没有得到数据,甚至再驱动里打印 log 也不生效,也是芯片问题吧。

第三个是根据 rt2860v2 驱动改的WiFi探针功能,我试了下,最后也没成功,感觉驱动没使用上,不知道是为什么。

第四个就是用钱喽,直接买了别人的WiFi探针板,还带蓝牙 mac 地址采集,小巧玲珑,就是我得从串口读取它得 json 数据,为此还专门写了一段代码来解析,这里算是成功拿到数据了。。。

下面讲讲我的过程吧!

使用PCAP库抓包

这里使用的是一个老哥的代码,博客写的很详细,可以参考下,我就不用在说了

https://blog.csdn.net/sunhaobo1996/category_7690613.html

ps. 我还在知网上下载了一些资料,有一篇论文写的很详细,不知道是不是这个老哥的。

修改无线固件

这个方法估计是我当时的救命草吧,因为我想无论你怎么使用 WiFi 功能,你的帧肯定得经过无线驱动吧,我是觉得这跟芯片关系不大,可是最后就是没成,我也很郁闷,下面详细讲。

修改驱动接受源码

修改无线驱动mac80211文件夹中的rx.c文件,获取 probe帧的mac。

vim build_dir/target-mips_34kc_uClibc-0.9.33.2/linux-ar71xx_generic/compat-wireless-2016-01-10/net/mac80211/rx.c

找到下面函数进行修改

static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, struct sk_buff *skb) 

具体修改地方看下面代码,改了三个地方

@@ -31,6 +31,7 @@
 #include "tkip.h"
 #include "wme.h"
 #include "rate.h"
+#include "probe_nl.h"
 
 static inline void ieee80211_rx_stats(struct net_device *dev, u32 len)
 {
 
@@ -3429,6 +3430,36 @@
 	ieee80211_invoke_rx_handlers(rx);
 	return true;
 }
+void collect_probe_info(u8 *addr,s8 signal)
+{
+  if(g_probed_unsend_num >= MAX_PROBED_NUM)
+	{
+    g_probed_unsend_num = 0;
+    return;
+  }
+	else
+	{
+    int i = 0;
+    bool flag =1;
+    for(i=0; i<g_probed_unsend_num; i++)
+		{
+      if(memcmp(g_probed_info[i],addr,6)==0)
+			{
+        flag = 0;
+				//update signal
+				//memcpy(g_probed_info[i], addr, 6);
+				memcpy(g_probed_info[i]+6, &signal, 1);
+        break;
+      }
+    }
+    if(flag)
+		{
+      memcpy(g_probed_info[g_probed_unsend_num], addr, 6);
+      memcpy(g_probed_info[g_probed_unsend_num]+6, &signal, 1);
+      g_probed_unsend_num++;
+    }
+  }
+}
 
 /*
  * This is the actual Rx frames handler. as it belongs to Rx path it must

 
@@ -3512,6 +3543,11 @@
 
 	prev = NULL;
 
+	if (ieee80211_is_probe_req(fc)/* && g_is_probe*/){
+      struct ieee80211_rx_status *sta = IEEE80211_SKB_RXCB(skb);
+      collect_probe_info(hdr->addr2,sta->signal);
+  }
+
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
 		if (!ieee80211_sdata_running(sdata))


编写netlink源码

接下来编写从内核传递到应用层的源码,在 mac80211文件夹下编写probe_nl.c、probe_nl.h两个文件

  • probe_nl.h
#ifndef _PROBE_NL_H_
#define _PROBE_NL_H_

#define MAX_PROBED_NUM 		200 //max saved sta device's mac number
#define PROBED_INFO 			7   //mac addr(6Byte)+ signal(1Byte)

extern unsigned char g_is_probe;
extern unsigned char g_probed_unsend_num;
extern unsigned char g_probed_info[MAX_PROBED_NUM][PROBED_INFO];

extern int probe_genetlink_init(void);
extern void probe_genetlink_exit(void);
#endif
  • probe_nl.c
#include <net/netlink.h>
#include <net/genetlink.h>
#include "probe_nl.h"

unsigned char g_is_probe;
unsigned char g_probed_unsend_num;
unsigned char g_probed_info[MAX_PROBED_NUM][PROBED_INFO];

//1. Registering a family
//1.1 Define the family, just create an instance of genl_family struct.
/* attributes */
enum {
	DOC_EXMPL_A_UNSPEC,
	DOC_EXMPL_A_MSG,
	__DOC_EXMPL_A_MAX,
};
#define DOC_EXMPL_A_MAX (__DOC_EXMPL_A_MAX - 1)
/* attribute policy */
static struct nla_policy doc_exmpl_genl_policy[DOC_EXMPL_A_MAX + 1] = {
	[DOC_EXMPL_A_MSG] = { .type = NLA_NUL_STRING },
};
/* genl_family definition */
static struct genl_family doc_exmpl_genl_family = {
	.id = GENL_ID_GENERATE,
	.hdrsize = 0,
	.name = "ProbeMacList",
	.version = 1,
	.maxattr = DOC_EXMPL_A_MAX,
};
//1.2 Define the operations for the family, which we do by creating at least one instance of the genl_ops structure.
/* commands: enumeration of all commands (functions) */
enum {
	DOC_EXMPL_C_UNSPEC,
	DOC_EXMPL_C_ECHO,
	__DOC_EXMPL_C_MAX,
};
#define DOC_EXMPL_C_MAX (__DOC_EXMPL_C_MAX - 1)

/* handler */
int doc_exmpl_echo(struct sk_buff *skb, struct genl_info *info)
{
	/* message handling code goes here, return 0 on success, negative value on failure */
	if (info == NULL)
		goto out;
	struct nlmsghdr *nlhdr;
	struct genlmsghdr *genlhdr;
	struct nlattr *nlh;
	
	char *str;
	struct sk_buff *skb_send;
	void *msg_head;
	int rc;
	unsigned char snd_buff[1024];

	nlhdr = nlmsg_hdr(skb);
	genlhdr = nlmsg_data(nlhdr);
	nlh = genlmsg_data(genlhdr);
	str = nla_data(nlh);
	
	if(*str == 's')
	{
		g_is_probe = 1;
		//printk("doc_exmpl_echo get: %s, start probe\n", str);
	}
	else
	{
		g_is_probe = 0;
		//printk("doc_exmpl_echo get: %s, stop probe\n", str);
	}
	
	skb_send = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
	if (skb_send == NULL)
		goto out;
	
	msg_head = genlmsg_put(skb_send, 0, info->snd_seq + 1, &doc_exmpl_genl_family, 0, DOC_EXMPL_C_ECHO);
	if (msg_head == NULL) {
		goto out;
	}

	printk("g_probed_unsend_num = %u\n", g_probed_unsend_num);
	memcpy(snd_buff, &g_probed_unsend_num, sizeof(g_probed_unsend_num));
	
	if(g_probed_info > 0)
	{
		memcpy(snd_buff + sizeof(g_probed_unsend_num), g_probed_info, g_probed_unsend_num*PROBED_INFO);
	}
	rc = nla_put(skb_send, DOC_EXMPL_A_MSG, sizeof(g_probed_unsend_num) + g_probed_unsend_num*PROBED_INFO, snd_buff);
	if (rc != 0)
		goto out;
	genlmsg_end(skb, msg_head);
	rc = genlmsg_unicast(genl_info_net(info), skb_send, info->snd_portid);
	
	g_probed_unsend_num = 0;

	return 0;
	
out:
	printk("An error occured in doc_exmpl_echo function.\n");
	return -1;
}

/* operation definition */
#if 1 //this need array
static struct genl_ops doc_exmpl_genl_ops_echo[] = 
{
	{
		.cmd = DOC_EXMPL_C_ECHO,
		.flags = 0,
		.policy = doc_exmpl_genl_policy,
		.doit = doc_exmpl_echo,
		.dumpit  = NULL,
	}
};
#else
static struct genl_ops doc_exmpl_genl_ops_echo = {
	.cmd = DOC_EXMPL_C_ECHO,
	.flags = 0,
	.policy = doc_exmpl_genl_policy,
	.doit = doc_exmpl_echo,
	.dumpit = NULL,
};
#endif

//1.3 Register ProbeMacList family with the Generic Netlink operation, and Register the operations for the family.
int probe_genetlink_init(void)
{
	int rc;
	g_is_probe = 0; 
	g_probed_unsend_num = 0;

	rc = genl_register_family_with_ops(&doc_exmpl_genl_family, doc_exmpl_genl_ops_echo);
	if (rc != 0)
		goto error;
	
	printk("Init probe_genetlink_init modules OK!\n");
	return 0;
error:
	printk("Init probe_genetlink_init modules error!\n");
	return -1;
}
void probe_genetlink_exit(void)
{
	printk("Init probe_genetlink_exit modules OK!\n");
	genl_unregister_family(&doc_exmpl_genl_family);
}
初始化netlink

修改mac80211文件夹中的 main.c,初始化时加入对 probe_nl 的初始化,具体修改看下面代码。

@@ -32,6 +32,7 @@
 #include "wep.h"
 #include "led.h"
 #include "debugfs.h"
+#include "probe_nl.h"
 
 void ieee80211_configure_filter(struct ieee80211_local *local)
 {
@@ -1235,12 +1236,18 @@
 	ret = ieee80211_iface_init();
 	if (ret)
 		goto err_netdev;
+	
+	ret = probe_genetlink_init();
+	if (ret)
+		goto err_netlink;
 
 	return 0;
  err_netdev:
 	rc80211_minstrel_ht_exit();
  err_minstrel:
 	rc80211_minstrel_exit();
+ err_netlink:
+	probe_genetlink_exit();	
 
 	return ret;
 }
@@ -1253,6 +1260,7 @@
 	ieee80211s_stop();
 
 	ieee80211_iface_exit();
+	probe_genetlink_exit();
 
 	rcu_barrier();
 }
增加对netlink源码的编译

修改 mac80211 文件夹中的 Makefile,编译 probe_nl 源码

@@ -21,6 +21,7 @@
 	ethtool.o \
 	rx.o \
 	spectmgmt.o \
+	probe_nl.o \
 	tx.o \
 	key.o \
 	util.o \

应用层代码

应用层代码比较简单,主要就是和内核创建连接,拿到内核传递出的数据,进行处理并显示。

  • wifi-probe.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <json-c/json.h>
#include <string.h>
#include <sys/wait.h>
#include <net/if.h>
#include <stdarg.h>
#include "genl-lib.h"

struct {
	time_t cur_time;
	unsigned char addr[6];
	char signal;
} kernel_sended_mac_list[MAX_PROBED_NUM];

int format_mac_info(void)
{
	int i;
	time_t now;
	time(&now);

	for(i=0;i<kernel_sended_pre_time;i++)
	{
		memcpy(kernel_sended_mac_list[i].addr,kernel_sended_info[i],6);
		memcpy(&(kernel_sended_mac_list[i].signal),kernel_sended_info[i]+6,sizeof(char));

		kernel_sended_mac_list[i].cur_time = now;
	}

	return 0;
}

void send_json_by_mosquitto(void)
{
	printf("kernel_sended_pre_time = %d\n\n",kernel_sended_pre_time);
	if(kernel_sended_pre_time == 0)
	return;

	int i;
	json_object *pData = json_object_new_array();
	json_object *pRespObj = json_object_new_object();
	for(i=0;i<kernel_sended_pre_time;i++)
	{
		json_object *jvalue = json_object_new_object();
		struct tm tm_fr;
		char fr_t[64]={0};
		char pmac[32]={0};
		char signal[16]={0};

		localtime_r(&kernel_sended_mac_list[i].cur_time, &tm_fr);
		strftime(fr_t,sizeof(fr_t),"%Y-%m-%d %H:%M:%S",&tm_fr);
		sprintf(pmac,"%02X:%02X:%02X:%02X:%02X:%02X",
		kernel_sended_mac_list[i].addr[0],kernel_sended_mac_list[i].addr[1],kernel_sended_mac_list[i].addr[2],
		kernel_sended_mac_list[i].addr[3],kernel_sended_mac_list[i].addr[4],kernel_sended_mac_list[i].addr[5]);
		sprintf(signal,"%d",kernel_sended_mac_list[i].signal);

		json_object_object_add(jvalue,"signal",json_object_new_string(signal));
		json_object_object_add(jvalue,"pt",json_object_new_string(fr_t));
		json_object_object_add(jvalue,"mac",json_object_new_string(pmac));

		json_object_array_add(pData,jvalue);
	}
	json_object_object_add(pRespObj,"probes",pData);

	const char *pJsonStr = json_object_get_string(pRespObj);
	printf("------------\n%s\n------------\n\n",pJsonStr);
	char cmdstring[200*64] = {0};
	sprintf(cmdstring, "echo \'%s\' > /tmp/tb-report", pJsonStr);
	system(cmdstring);
	system("/bin/sh /usr/sbin/wifi-report /tmp/tb-report && rm -rf /tmp/tb-report");

	return;
}
void init_golbal_params(void)
{
	kernel_sended_pre_time = 0;
	memset(kernel_sended_mac_list,0,sizeof(kernel_sended_mac_list));
	memset(kernel_sended_info,0,sizeof(kernel_sended_info));
}
int main()
{
	int sock_fd;
	sock_fd = genl_socket_init();
	if(sock_fd < 0){
		printf("create_nl_socket create failure\n");
		return 0;
	}

	int family_id = genl_get_family_id(sock_fd, "ProbeMacList");

	for(;;){
		init_golbal_params();
		genl_send_msg(sock_fd, family_id, DOC_EXMPL_C_ECHO, DOC_EXMPL_A_MSG, "s");
		genl_rcv_msg(family_id,sock_fd);
		format_mac_info();
		send_json_by_mosquitto();
		sleep(5);
	}
}
  • genl-lib.h
#ifndef _GENL_LIB_
#define _GENL_LIB_
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <linux/genetlink.h>

enum {
	DOC_EXMPL_A_UNSPEC,
	DOC_EXMPL_A_MSG,
	__DOC_EXMPL_A_MAX,
};
#define DOC_EXMPL_A_MAX (__DOC_EXMPL_A_MAX - 1)
enum {
	DOC_EXMPL_C_UNSPEC,
	DOC_EXMPL_C_ECHO,
	__DOC_EXMPL_C_MAX,
};
#define DOC_EXMPL_C_MAX (__DOC_EXMPL_C_MAX - 1)


#define MAX_MSG_SIZE 1024*2

#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
#define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN))

typedef struct msgtemplate {
    struct nlmsghdr nlh;
    struct genlmsghdr gnlh;
    char data[MAX_MSG_SIZE];
} msgtemplate_t;


int genl_socket_init(void);
int genl_get_family_id(int sock_fd, char *family_name);
int genl_send_msg(int sock_fd, u_int16_t family_id, u_int8_t genl_cmd, u_int16_t nla_type,void *nla_data);
void genl_rcv_msg(int family_id, int sock_fd);

//From Kernel define
#define MAX_PROBED_NUM 		200 //max saved sta device's mac number
#define PROBED_INFO 			7   //mac addr(6Byte)+ signal(1Byte)

extern unsigned char kernel_sended_pre_time;
extern unsigned char kernel_sended_info[MAX_PROBED_NUM][PROBED_INFO];

#endif
  • genl-lib.c
#include "genl-lib.h"

unsigned char kernel_sended_pre_time; // 0-200
unsigned char kernel_sended_info[MAX_PROBED_NUM][PROBED_INFO];

int genl_get_family_id(int sock_fd, char *family_name)
{
	msgtemplate_t ans;
	int id, rc;
	struct nlattr *na;
	int rep_len;
	rc = genl_send_msg(sock_fd, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, CTRL_ATTR_FAMILY_NAME, (void *)family_name);
	rep_len = recv(sock_fd, &ans, sizeof(ans), 0);
	if (rep_len < 0) {
		return 1;
	}
	if (ans.nlh.nlmsg_type == NLMSG_ERROR || !NLMSG_OK((&ans.nlh), rep_len))
	{
		return 1;
	}
	na = (struct nlattr *) GENLMSG_DATA(&ans);
	na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len));
	if (na->nla_type == CTRL_ATTR_FAMILY_ID) 
	{
		id = *(__u16 *) NLA_DATA(na);
	} else {
		id = 0;
	}
	return id;
}

int genl_send_msg(int sock_fd, u_int16_t family_id, u_int8_t genl_cmd, u_int16_t nla_type,void *nla_data)
{
	struct nlattr *na;
	struct sockaddr_nl dst_addr;
	int r, buflen;
	char *buf;
	int nla_len = strlen(nla_data)+1;
	msgtemplate_t msg;
	if (family_id == 0) {
		return 0;
	}
	msg.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
	msg.nlh.nlmsg_type = family_id;
	msg.nlh.nlmsg_flags = NLM_F_REQUEST;
	msg.nlh.nlmsg_seq = 0;
	msg.nlh.nlmsg_pid = getpid();
	msg.gnlh.cmd = genl_cmd;
	msg.gnlh.version = 0x1;
	na = (struct nlattr *) GENLMSG_DATA(&msg);
	na->nla_type = nla_type;
	na->nla_len = nla_len + 1 + NLA_HDRLEN;
	memcpy(NLA_DATA(na), nla_data, nla_len);
	msg.nlh.nlmsg_len += NLMSG_ALIGN(na->nla_len);
	buf = (char *) &msg;
	buflen = msg.nlh.nlmsg_len ;
	memset(&dst_addr, 0, sizeof(dst_addr));
	dst_addr.nl_family = AF_NETLINK;
	dst_addr.nl_pid = 0;
	dst_addr.nl_groups = 0;
	while ((r = sendto(sock_fd, buf, buflen, 0, (struct sockaddr *) &dst_addr, sizeof(dst_addr))) < buflen) 
	{
		if (r > 0) {
			buf += r;
			buflen -= r;
		} else if (errno != EAGAIN) {
			return -1;
		}
	}
	return 0;
}
void genl_rcv_msg(int family_id, int sock_fd)
{
	int ret;
	struct msgtemplate msg;
	struct nlattr *na;
	ret = recv(sock_fd, &msg, sizeof(msg), 0);
	if (ret < 0) {
		printf("error receiving reply message via Netlink length < 0\n");
		return;
	}
	/* Validate response message */
	if (msg.nlh.nlmsg_type == NLMSG_ERROR) { /* error */
		printf("NLMSG_ERROR error received NACK - leaving \n");
		return;
	}
	if (msg.nlh.nlmsg_type == family_id && family_id != 0) 
	{
		na = (struct nlattr *) GENLMSG_DATA(&msg);
		unsigned char * result = (unsigned char *)NLA_DATA(na);
		int i;
		memcpy(&kernel_sended_pre_time, result,sizeof(kernel_sended_pre_time));
		if(kernel_sended_pre_time > 0 && kernel_sended_pre_time <= MAX_PROBED_NUM )
		{
			memcpy(kernel_sended_info,result + sizeof(kernel_sended_pre_time),kernel_sended_pre_time*PROBED_INFO);
		}
	}
}

int genl_socket_init(void)
{
	struct sockaddr_nl saddr;
	int fd;

	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
	if (fd < 0){
		perror("genl_init: socket create error\n");
		return -1;
	}

	memset(&saddr, 0, sizeof(saddr));
	saddr.nl_family = AF_NETLINK;
	saddr.nl_groups = 0;
	saddr.nl_pid = getpid();
	if (bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
	{
		perror("bind failed\n");
		close(fd);
		return -1;
	}

	return fd;
}
  • 源码 Makefile
LDFLAGS = -ljson-c 

wifi-probe:wifi-probe.o genl-lib.o
	$(CC) $(LDFLAGS) wifi-probe.o genl-lib.o -o wifi-probe
genl-lib.o:genl-lib.c
	$(CC) $(CFLAGS) -c genl-lib.c
wifi-probe.o:wifi-probe.c
	$(CC) $(CFLAGS) -c wifi-probe.c

clean:
	rm *.o wifi-probe
  • 外层 Makefile
#
# Top level makefile for example application
#

include $(TOPDIR)/rules.mk

PKG_NAME:=wifi-probe
PKG_VERSION:=1.0.0
PKG_RELEASE:=1

include $(INCLUDE_DIR)/package.mk

define Package/wifi-probe
  SECTION:=utils
  DEPENDS:= +libjson-c
  CATEGORY:=Utilities
  TITLE:= wifi-probe userspace applications
endef

define Build/Prepare
	mkdir -p $(PKG_BUILD_DIR)
	$(CP) ./src/* $(PKG_BUILD_DIR)
endef

define Build/Configure
endef

TARGET_CFLAGS += $(FPIC)

define Package/wifi-probe/install
	$(INSTALL_DIR) $(1)/bin
	$(INSTALL_DIR) $(1)/usr/sbin
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/wifi-probe $(1)/bin/
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/wifi-report $(1)/usr/sbin/
endef

$(eval $(call BuildPackage,wifi-probe))
备注

感觉自己有点小偷的感觉啦,这里都是复制别人的代码,感觉不太好还是把参考的文章贴一下吧。

这个写的很详细,贴了GitHub源码

https://blog.csdn.net/bingdele/article/details/96442385

https://hub.fastgit.org/wesley-fly/wifi-probe

这个应该是最早的版本吧,可惜没开源,图都绿了

http://blog.sina.com.cn/s/blog_636a55070102wpfx.html

基于rt2860v2的wifi探针

rt2860v2 是一个开源的驱动,我觉得大致原理应该是和上面的方法差不多,就是有人做成开源的驱动了,别人博客也写的很清楚了,我就不多说了。

https://blog.csdn.net/lixuande19871015/article/details/71601363

结语

虽然我最后的WiFi探针功能是通过买别人的小板子做成的,但是上面这些方法应该是网上常见的方法了,在重新编写博客的时候,我都觉得可能是我当时太年轻,没仔细按博客做,如果读者有需要,可以试试上面方法,我是不想再做这样的工作了,但是说不定你的板子就可以了!

end

完美撒花

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值