C++实现802.1x客户端

图例:tp-link802.1x认证客户端,需要填入用户名、密码、网卡。

  1. 选择网卡

(1)添加网卡信息结构体

struct NetcardInfo {
  char name[NET_CARD_NAME_MAX_LENGTH];  // 用于pcap_open_live()函数,格式例: \Device\NPF_{D89BD73E-7192-407B-9871-E315194968F8}
  char description[NET_CARD_DESCRIPTION_MAX_LENGTH];  // 界面描述,同tp-link表述形式
  u_char mac[MAC_SIZE];  // 网卡mac地址,用于组二层包头
  bool is_physical;  // 是否物理网卡
  bool is_wireless;  // 是否无线网卡
};
(2)遍历所有网卡信息

通过函数GetAdaptersInfo遍历所有网卡,去除非物理与无线网卡,界面显示可选择的有线网卡(tp-link未去除,会显示所有网卡,包括无线和虚拟网卡)

具体获取:


sprintf_s(netcard.name, sizeof(netcard.name) - 1, "\\Device\\NPF_%s", adapter->AdapterName);
sprintf_s(netcard.description, sizeof(netcard.description) - 1, adapter->Description);
for (UINT i = 0; i < adapter->AddressLength && i < 6; ++i) {
    netcard.mac[i] = adapter->Address[i];
}
netcard.is_physical = (strstr(adapter->Description, "PCI") > 0);
netcard.is_wireless = (adapter->Type == 71);

  1. 基础函数调用

(1)pcap_open_live获取网卡handle用于通信

pcap_t* pcap_open_live(char* device, int snaplen, int promisc, int to_ms, char* ebuf);

device: 网卡名,格式 \\Device\\NPF_XXXX,如果传入NULL或"any",对所有接口进行捕获

snaplen: 设置每个数据包的捕捉长度,上限MAXIMUM_SNAPLEN

promisc: 是否打开混杂模式

to_ms: 设置获取数据包时的超时时间(ms)(时间过长导致捕获阻塞函数pcap_next_ex延迟退出?)


char error[PCAP_ERRBUF_SIZE];
handle = pcap_open_live("\\Device\\NPF_XXXX", 65536, 1, 20, error);
(2)pcap_compile && pcap_setfilter设置捕获过滤

  char filter[128];
  std::string src_dst = "dst";  // "src"
  sprintf_s(filter, "(ether proto 0x888e) and (ether %s host %02x:%02x:%02x:%02x:%02x:%02x)",
    src_dst.c_str(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

  struct bpf_program    fcode;
  pcap_compile(adapter_hander_, &fcode, filter, 1, 0xff);
  pcap_setfilter(adapter_hander_, &fcode);
(3)pcap_next_ex获取报文

void ProcessRecvData(Process* processer) {
  if (!processer) {
    return;
  }

  pcap_t* adapter =processer->GetAdapterHandle();
  if (!adapter) {
    return;
  }

  struct pcap_pkthdr *header;
  const unsigned char *captured;
  while (true) {
    if (connection_stop) {
      return;
    }

    int ret = pcap_next_ex(adapter, &header, &captured);
    if (1 == ret) {
      processer->HandleRecvData(captured);
    }
    else {
      Sleep(20);
    }
  }
}

//recv_thread = new std::thread(ProcessRecvData, this);
(4)pcap_sendpacket发送数据包

int pcap_sendpacket(pcap_t p, u_char buf, int size);

buf是发送数据包的内容缓冲区首地址,目的mac+源mac+协议类型(802.1x是0x88 8e)+802.1x authentication ...

目的mac:广播mac地址{ 0xff,0xff,0xff,0xff,0xff,0xff }或多播mac地址{ 0x01,0x80,0xc2,0x00,0x00,0x03 },首次收到回复后可保存下ac的mac地址并通过pcap_setfilter设置捕获过滤

(5)pcap_close

  1. 数据包处理

(1)交互流程

client->AP: EAPOL START

AP->client: Request Identity

client->AP: Response Identity

AP->client: Request MD5-challenge EAP || ...

client->AP: Response Legacy Nak // 举例仅支持PEAP

AP->client: Request Protected PEAP

client->AP: Client Hello

...

AP->client: Server Hello

...

client->AP: TLS Data

AP->client: TLS Data

...

AP->client: Repeat // 可能出现

client->AP: Response

AP->client: Success

(2)加密数据处理

struct PeapPara {
  Tls* tls;

  u_char *in_buf;
  u_char *out_buf;
  int written;
  int read;
  int phase;

  u_char tk[PEAP_TLV_TK_LEN];
  u_char ipmk[PEAP_TLV_IPMK_LEN];
  u_char nonce[PEAP_TLV_NONCE_LEN];
  ChapMs chap;
};

加密数据分为TLS与CHAP-MS两层,TLS使用BIO_read && BIO_write读写,CHAP-MS需要单独类处理,建议参考PPP协议的开源代码

(3)特殊情况处理

交互流程中,最后可能存在一步Repeat

调试过程中,经过解密的TLS数据,根据首位EAP type的值,分为IDentity,Tls,MsChapV2处理,然而最后一步Response Identity 后一直返回failure。查找资料才知道这边是AP要求重复Request的值,详细参考Packet15

4、参考链接

802.1X协议及Radius协议(二)

IEEE 802.1X-PEAP认证过程分析(抓包)

最详细的802.1x认证原理及eap-md5的认证授权计费

SSL双向认证的认证模式设置问题

EAP-MSCHAPv2

EAP-PEAP with Mschapv2: Decrypted and Decoded

microsoft's PEAP version 0 (Implementation in Windows XP SP1)

PPP协议

5、拓展

以上是WIFI连接的抓包

流程与有线连接基本相同,不过每个包的802.1x WIFI头较有线的源mac目的mac复杂一些

也许去除二层头后处理逻辑可以复用?

留待后续学习后解答...

6、更新

具体实现可参考我的github源码 wwp1122/net_connector (github.com)

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值