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
完美撒花