Linux下的arp抓包程序代码,Linux系统无线网络抓包程序(分析手机WIFI MAC地址)

前面讲述了使用tcpdump和wireshark抓WIFI包,但这只是使用工具的层面,再深一层则是自己写代码实现这个功能。本文在前面文章《Linux系统有线网络抓包程序》的基础上添加实现无线网络的抓包功能。

首先要介绍ieee802.11的帧格式,只有知道帧格式才能正确解析对应字段,拿到我们感兴趣的信息。其次介绍Linux raw socket编程抓包。最后解析ieee802.11数据包,从而获取到MAC地址。实际上,从数据包中可以得到很多信息,这些信息就是后续需要继续进行的事了。

一、ieee802.11帧格式

ieee802.11帧格式如下图所示:

0818b9ca8b590ca3270a3433284dd417.png

上图来自ieee802.11标准文档《802.11-2012.pdf》的8.2.3小节。它比802.3以太帧不同。帧类型有很三大类:数据帧、管理帧、控制帧。在手机WIFI开启扫描热点、连接热点、过程主要涉及管理帧。比如手机在开启WIFI时,会向周边发出probe request帧,热点会回应probe response帧,其中probe request帧头部包含了手机MAC号信息。抓到此包,就能解析出手机MAC号了。不同的帧的Frame Body不同,但这不是本文关注的重点。关于帧类型,具体参考ieee802.11标准文档8.2.4.1.3 (Type and Subtype fields)小节。

二、C实现socket抓包

Linux系统抓包使用SOCK_RAW方式,类型为ETH_P_ALL(表示抓取所有类型的帧,不管是IP帧还是ARP帧)。下面给出代码重要函数代码片段。为减小文章篇幅,保留主要代码函数,至于完整代码,请参阅文章后面的附录。

1、设置混杂模式

抓包工具都会开启混杂模式(promisc),下面是代码:

// 混杂模式

bool set_promisc_mode(const char* eth, bool promisc)

{

int org_errno = 0;

int fd;

struct ifreq ifreq;

if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)

return false;

memset(&ifreq, 0, sizeof(ifreq));

strncpy(ifreq.ifr_name, eth, IF_NAMESIZE - 1);

ioctl(fd, SIOCGIFFLAGS, &ifreq);

// check if eth is up

if (!(ifreq.ifr_flags & IFF_UP))

{

printf("%s is not up yet.\n", eth);

return false;

}

if (promisc)

ifreq.ifr_flags |= IFF_PROMISC;

else

ifreq.ifr_flags &= ~IFF_PROMISC;

ioctl(fd, SIOCSIFFLAGS, &ifreq);

if (close(fd))

return false;

return true;

}

2、初始化socket

初始化socket包括创建RAW socket,绑定指定网卡。

int init_socket(const char* eth)

{

int ret = 0;

int fd = -1;

// 混杂模式

if (!set_promisc_mode(eth, true))

{

//printf("set %s to promisc mode failed.\n", eth);

return -1;

}

// 注意与下面绑定时协议一致

fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

// 绑定网卡

struct ifreq req;

strcpy(req.ifr_name, eth);

ioctl(fd, SIOCGIFINDEX, &req);

struct sockaddr_ll addr;

addr.sll_family = PF_PACKET;

addr.sll_ifindex = req.ifr_ifindex;

addr.sll_protocol = htons(ETH_P_ALL);

ret = bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_ll));

return fd;

}

3、获取网卡信息

int get_hwinfo(int fd, char* eth, unsigned char* mac)

{

struct ifreq ifr;

memset(&ifr, 0, sizeof(ifr));

strncpy(ifr.ifr_name, eth, IFNAMSIZ - 1);

ifr.ifr_name[IFNAMSIZ - 1] = '\0';

if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0)

{

printf("Could not get arptype\n");

return -1;

}

printf("ARPTYPE %d\n", ifr.ifr_hwaddr.sa_family);

memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);

return ifr.ifr_hwaddr.sa_family;

}

注意,函数返回的sa_family十分重要,它是判断抓取的帧类型的依据。跟踪SIOCGIFHWADDR调用过程,发现内核驱动将dev->dev_addr赋值给ifr->ifr_hwaddr.sa_data,而dev->type赋值给ifr->ifr_hwaddr.sa_family。

4、接收数据

一般网络接收数据都会使用select模型,使用recv即可收到内核传递的数据。

int receive_packet(int socket)

{

int ret = 0;

struct timeval tv;

static fd_set read_fds;

tv.tv_sec = 0;

tv.tv_usec = 100;

FD_ZERO(&read_fds);

FD_SET(socket, &read_fds);

ret = select(socket+1, &read_fds, NULL, NULL, &tv);

if (ret == -1 && errno == EINTR) /* interrupted */

return -1;

if (ret == 0)

return -1;

else if (ret < 0)

return -1;

if (FD_ISSET(socket, &read_fds))

{

memset(buffer, '\0', BUFFER_SIZE);

ret = recv(socket, buffer, BUFFER_SIZE, MSG_DONTWAIT);

if (ret <= 0)

return -1;

//printf("--recv len: %d\n", ret);

if (debug_level)

{

dump(buffer, ret);

printf("====================\n");

}

if (arphrd == 1)

parse_packet(buffer, ret); // ieee802.3包

else if (arphrd == 802 || arphrd == 803) // ieee802.11包

parse_packet_wlan(buffer, ret);

}

return 0;

}函数最后根据arphrd类型调用不同的解析函数。本文只针对无线网络包解析。

三、解析

下图是笔者手机发的probe request帧截图(使用tcpdump抓包,再用wireshark查看)。

0818b9ca8b590ca3270a3433284dd417.png

其中第一部分是radiotap头部,第二部分是probe request头部,第三部分是probe request的frame body。

PS:本文所述代码工程将会不断完善,并择机上传至github。

附代码:

1、main.c

2、 李迟 2016.11.01 夜

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值