乐于分享
好东西不私藏

跨平台底层网络库libdnet源码分析系列(十三)

跨平台底层网络库libdnet源码分析系列(十三)

源码分析mettle后门工具学习 所使用的依赖库

Windows内核分支深度分析与限制分析

目录

  1. Windows平台架构概述
  2. 内核分支实现深度分析
  3. 网络接口管理模块
  4. IP与路由模块
  5. ARP模块
  6. 防火墙模块
  7. 以太网模块限制
  8. Windows平台限制详解
  9. 各平台实现对比
  10. 变通方案与最佳实践
  11. 附录:完整代码示例

Windows平台架构概述

1.1 平台选择机制

libdnet通过编译时条件编译选择特定平台的实现:

  1. // include/dnet/os.h
  2. #ifdef _WIN32
  3. #include<winsock2.h>
  4. #include<windows.h>
  5. typedefintptr_tdnet_socket_t;
  6. #else
  7. #include<sys/socket.h>
  8. #include<netinet/in.h>
  9. typedefintdnet_socket_t;
  10. #endif

1.2 Windows特定文件清单

模块
Windows实现文件
替代实现
网络接口
intf-win32.c intf-bsd.c

intf-linux.c
IP
ip-win32.c ip.c

 (通用)
路由
route-win32.c route-bsd.c

route-linux.c
ARP
arp-win32.c arp-bsd.c

arp-linux.c
防火墙
fw-pktfilter.c fw-ipfw.c

fw-pf.cfw-ipf.cfw-ipchains.c
以太网
eth-win32.c

 (存根)
eth-bsd.c

eth-linux.ceth-dlpi.c

1.3 核心设计理念

  1. 零依赖设计:不依赖WinPcap/Npcap,使用原生Windows API
  2. 版本兼容性:通过动态加载支持Windows XP到Windows 11
  3. 权限要求:部分功能需要管理员权限(原始套接字、路由表修改)
  4. Unicode处理:正确处理宽字符( wchar_t)与多字节字符的转换

内核分支实现深度分析

2.1 Windows内核网络架构

libdnet在Windows上的实现直接利用Windows内核提供的网络API,架构层次如下:

  1. ┌─────────────────────────────────────────┐
  2.         libdnet用户态API                 
  3. ├─────────────────────────────────────────┤
  4. WindowsSockets2(Winsock2)
  5. ├─────────────────────────────────────────┤
  6.    IP Helper API (iphlpapi.dll)
  7. -GetAdaptersAddresses
  8. -GetIpForwardTable/2
  9. -GetIpNetTable
  10. ├─────────────────────────────────────────┤
  11. Windows网络驱动(ndis.sys)
  12. ├─────────────────────────────────────────┤
  13. 网络接口卡驱动
  14. └─────────────────────────────────────────┘

2.2 数据结构映射

2.2.1 网络接口结构

  1. // intf-win32.c
  2. struct intf_handle {
  3. struct ifcombo ifcombo[MIB_IF_TYPE_MAX];// 按类型分组的接口索引
  4.     IP_ADAPTER_ADDRESSES *iftable;// Windows接口表
  5. };
  6. struct ifcombo {
  7. struct{
  8.         DWORD ipv4;// IPv4接口索引
  9.         DWORD ipv6;// IPv6接口索引
  10. }*idx;
  11. int cnt;// 数量
  12. int max;// 容量
  13. };

关键设计点

  • 将Windows的 IP_ADAPTER_ADDRESSES结构映射到libdnet的 intf_entry
  • 使用 ifcombo数组按接口类型(以太网、PPP、环回等)分组
  • 支持IPv4和IPv6双栈

2.2.2 路由表结构

  1. // route-win32.c
  2. struct route_handle {
  3.     HINSTANCE iphlpapi;// iphlpapi.dll模块句柄
  4.     MIB_IPFORWARDTABLE *ipftable;// IPv4路由表(XP/2003)
  5.     MIB_IPFORWARD_TABLE2 *ipftable2;// IPv4/IPv6路由表(Vista+)
  6. };

版本兼容策略

  • 动态加载 GetIpForwardTable2(仅Vista+可用)
  • 保留旧版 GetIpForwardTable以支持XP/2003

网络接口管理模块

3.1 GetAdaptersAddresses深度解析

3.1.1 完整实现代码

  1. // intf-win32.c:220-268
  2. staticint
  3. _refresh_tables(intf_t*intf)
  4. {
  5.     IP_ADAPTER_ADDRESSES *p;
  6.     DWORD ret;
  7.     ULONG len;
  8.     p = NULL;
  9.     len =16384;// 初始大缓冲区,避免Windows 2003的bug
  10. do{
  11.         free(p);
  12.         p = malloc(len);
  13. if(== NULL)
  14. return(-1);
  15.         ret =GetAdaptersAddresses(
  16.             AF_UNSPEC,// IPv4和IPv6
  17.             GAA_FLAG_INCLUDE_PREFIX |// 包含前缀信息
  18.             GAA_FLAG_SKIP_ANYCAST |
  19.             GAA_FLAG_SKIP_MULTICAST,
  20.             NULL,
  21.             p,
  22. &len
  23. );
  24. }while(ret == ERROR_BUFFER_OVERFLOW);
  25. if(ret != NO_ERROR){
  26.         free(p);
  27. return(-1);
  28. }
  29.     intf->iftable = p;
  30. // 映射Windows索引到libdnet索引
  31. for(= intf->iftable; p != NULL; p = p->Next){
  32. int type;
  33.         type = _if_type_canonicalize(p->IfType);
  34. if(type < MIB_IF_TYPE_MAX)
  35.             _ifcombo_add(&intf->ifcombo[type], p->IfIndex, p->Ipv6IfIndex);
  36. else
  37. return(-1);
  38. }
  39. return(0);
  40. }

3.1.2 Windows 2003缓冲区Bug处理

问题描述: 在Windows 2003上, GetAdaptersAddresses的行为异常:

  • 首次调用缓冲区不足:返回 ERROR_BUFFER_OVERFLOW
  • 后续调用缓冲区不足:返回 ERROR_INVALID_PARAMETER

解决方案

  1. // 首次调用使用大缓冲区(16KB)
  2. len =16384;
  3. do{
  4. // ...
  5. }while(ret == ERROR_BUFFER_OVERFLOW);

3.1.3 宽字符转换处理

  1. // intf-win32.c:40-48
  2. staticchar* strncpy_wchar(char*dst, PWCHAR src,size_t n)
  3. {
  4. size_t wlen = wcslen(src)+2;
  5. char*tmp = calloc(1, wlen);
  6. WideCharToMultiByte(CP_ACP,0, src,-1, tmp, wlen, NULL, NULL);
  7.     strncpy(dst, tmp, n);
  8.     free(tmp);
  9. return dst;
  10. }

调用示例

  1. // 转换友好名称
  2. strncpy_wchar(friendly_name, a->FriendlyName, MAX_INTERFACE_NAME_LEN);
  3. // 转换驱动描述
  4. strncpy_wchar(entry->driver_name, a->Description,sizeof(entry->driver_name));

3.2 接口类型标准化

3.2.1 Windows MIB类型到libdnet类型的映射

  1. // intf-win32.c:50-72
  2. staticchar*
  3. _ifcombo_name(int type)
  4. {
  5. char*name ="eth";// 默认以太网
  6. if(type == IF_TYPE_ISO88025_TOKENRING){
  7.         name ="tr";
  8. }elseif(type == IF_TYPE_PPP){
  9.         name ="ppp";
  10. }elseif(type == IF_TYPE_SOFTWARE_LOOPBACK){
  11.         name ="lo";
  12. }elseif(type == IF_TYPE_TUNNEL){
  13.         name ="tun";
  14. }
  15. return(name);
  16. }

3.2.2 接口标志映射

  1. // intf-win32.c:164-172
  2. entry->intf_flags =0;
  3. if(a->OperStatus==IfOperStatusUp)
  4.     entry->intf_flags |= INTF_FLAG_UP;
  5. if(a->IfType== IF_TYPE_SOFTWARE_LOOPBACK)
  6.     entry->intf_flags |= INTF_FLAG_LOOPBACK;
  7. else
  8.     entry->intf_flags |= INTF_FLAG_MULTICAST;

3.3 前缀长度查找

Windows XP/Vista前缀信息存储方式不同:

  1. // intf-win32.c:196-202
  2. bits =0;
  3. for(prefix = a->FirstPrefix; prefix != NULL; prefix = prefix->Next){
  4. if(prefix->Address.lpSockaddr->sa_family == addr->Address.lpSockaddr->sa_family){
  5.         bits =(unsignedshort) prefix->PrefixLength;
  6. break;
  7. }
  8. }

Vista及以后:使用 OnLinkPrefixLength成员


IP与路由模块

4.1 IP模块:原始套接字实现

4.1.1 完整源码分析

  1. // ip-win32.c:24-49
  2. ip_t*
  3. ip_open(void)
  4. {
  5.     BOOL on;
  6. ip_t*ip;
  7. if((ip = calloc(1,sizeof(*ip)))!= NULL){
  8. // 初始化Winsock
  9. if(WSAStartup(MAKEWORD(2,2),&ip->wsdata)!=0){
  10.             free(ip);
  11. return(NULL);
  12. }
  13. // 创建原始套接字
  14. if((ip->fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==
  15.             INVALID_SOCKET)
  16. return(ip_close(ip));
  17. // 设置包含IP头选项
  18.         on = TRUE;
  19. if(setsockopt(ip->fd, IPPROTO_IP, IP_HDRINCL,
  20. (constchar*)&on,sizeof(on))== SOCKET_ERROR){
  21. SetLastError(ERROR_NETWORK_ACCESS_DENIED);
  22. return(ip_close(ip));
  23. }
  24.         ip->sin.sin_family = AF_INET;
  25.         ip->sin.sin_port = htons(666);
  26. }
  27. return(ip);
  28. }

4.1.2 IP头包含选项

  1. // 设置IP_HDRINCL允许应用构建完整IP头
  2. setsockopt(ip->fd, IPPROTO_IP, IP_HDRINCL,&on,sizeof(on));

作用:应用层提供完整IP头,系统不再自动添加

4.1.3 数据包发送

  1. // ip-win32.c:51-63
  2. ssize_t
  3. ip_send(ip_t*ip,constvoid*buf,size_t len)
  4. {
  5. struct ip_hdr *hdr =(struct ip_hdr *)buf;
  6.     ip->sin.sin_addr.s_addr = hdr->ip_src;
  7. if((len = sendto(ip->fd,(constchar*)buf, len,0,
  8. (struct sockaddr *)&ip->sin,sizeof(ip->sin)))!= SOCKET_ERROR)
  9. return(len);
  10. return(-1);
  11. }

4.2 路由模块:动态API加载

4.2.1 模块初始化

  1. // route-win32.c:36-47
  2. route_t*
  3. route_open(void)
  4. {
  5. route_t*r;
  6.     r = calloc(1,sizeof(route_t));
  7. if(== NULL)
  8. return NULL;
  9. // 获取iphlpapi.dll模块句柄
  10.     r->iphlpapi =GetModuleHandle("iphlpapi.dll");
  11. return r;
  12. }

4.2.2 版本兼容的路由表遍历

  1. // route-win32.c:256-270
  2. int
  3. route_loop(route_t*r, route_handler callback,void*arg)
  4. {
  5.     GETIPFORWARDTABLE2 GetIpForwardTable2;
  6. // 动态加载GetIpForwardTable2(仅Vista+)
  7. GetIpForwardTable2= NULL;
  8. if(r->iphlpapi != NULL)
  9. GetIpForwardTable2=(GETIPFORWARDTABLE2)
  10. GetProcAddress(r->iphlpapi,"GetIpForwardTable2");
  11. if(GetIpForwardTable2== NULL)
  12. return route_loop_getipforwardtable(r, callback, arg);// XP/2003
  13. else
  14. return route_loop_getipforwardtable2(GetIpForwardTable2, r, callback, arg);// Vista+
  15. }

4.2.3 IPv4路由表读取(XP/2003)

  1. // route-win32.c:142-197
  2. staticint
  3. route_loop_getipforwardtable(route_t*r, route_handler callback,void*arg)
  4. {
  5. struct route_entry entry;
  6. intf_t*intf;
  7.     ULONG len;
  8. int i, ret;
  9. // 动态调整缓冲区大小
  10. for(len =sizeof(r->ipftable[0]);;){
  11. if(r->ipftable)
  12.             free(r->ipftable);
  13.         r->ipftable = malloc(len);
  14. if(r->ipftable == NULL)
  15. return(-1);
  16.         ret =GetIpForwardTable(r->ipftable,&len, FALSE);
  17. if(ret == NO_ERROR)
  18. break;
  19. elseif(ret != ERROR_INSUFFICIENT_BUFFER)
  20. return(-1);
  21. }
  22.     intf = intf_open();
  23.     ret =0;
  24. for(=0; i <(int)r->ipftable->dwNumEntries; i++){
  25. // 填充路由条目
  26.         entry.route_dst.addr_type = ADDR_TYPE_IP;
  27.         entry.route_dst.addr_bits = IP_ADDR_BITS;
  28.         entry.route_gw.addr_type = ADDR_TYPE_IP;
  29.         entry.route_gw.addr_bits = IP_ADDR_BITS;
  30.         entry.route_dst.addr_ip = r->ipftable->table[i].dwForwardDest;
  31.         addr_mtob(&r->ipftable->table[i].dwForwardMask, IP_ADDR_LEN,
  32. &entry.route_dst.addr_bits);
  33.         entry.route_gw.addr_ip = r->ipftable->table[i].dwForwardNextHop;
  34.         entry.metric = r->ipftable->table[i].dwForwardMetric1;
  35. // 查找接口名称
  36.         entry.intf_name[0]='\0';
  37. if(intf_get_index(intf,&intf_entry,
  38.             AF_INET, r->ipftable->table[i].dwForwardIfIndex)==0){
  39.             strlcpy(entry.intf_name, intf_entry.intf_name,
  40. sizeof(entry.intf_name));
  41. }
  42. if((ret =(*callback)(&entry, arg))!=0)
  43. break;
  44. }
  45.     intf_close(intf);
  46. return ret;
  47. }

4.2.4 IPv4/IPv6路由表读取(Vista+)

  1. // route-win32.c:199-254
  2. staticint
  3. route_loop_getipforwardtable2(GETIPFORWARDTABLE2 GetIpForwardTable2,
  4. route_t*r, route_handler callback,void*arg)
  5. {
  6. struct route_entry entry;
  7. intf_t*intf;
  8.     ULONG i;
  9. int ret;
  10. // 获取IPv4和IPv6路由表
  11.     ret =GetIpForwardTable2(AF_UNSPEC,&r->ipftable2);
  12. if(ret != NO_ERROR)
  13. return(-1);
  14.     intf = intf_open();
  15.     ret =0;
  16. for(=0; i < r->ipftable2->NumEntries; i++){
  17. struct intf_entry intf_entry;
  18.         MIB_IPFORWARD_ROW2 *row;
  19.         MIB_IPINTERFACE_ROW ifrow;
  20.         ULONG metric;
  21.         row =&r->ipftable2->Table[i];
  22. // 填充路由条目(支持IPv4和IPv6)
  23.         addr_ston((struct sockaddr *)&row->DestinationPrefix.Prefix,
  24. &entry.route_dst);
  25.         entry.route_dst.addr_bits = row->DestinationPrefix.PrefixLength;
  26.         addr_ston((struct sockaddr *)&row->NextHop,&entry.route_gw);
  27. // 查找接口名称
  28.         entry.intf_name[0]='\0';
  29. if(intf_get_index(intf,&intf_entry,
  30.             row->DestinationPrefix.Prefix.si_family,
  31.             row->InterfaceIndex)==0){
  32.             strlcpy(entry.intf_name, intf_entry.intf_name,
  33. sizeof(entry.intf_name));
  34. }
  35. // 计算度量值(接口度量 + 路由度量)
  36.         ifrow.Family= row->DestinationPrefix.Prefix.si_family;
  37.         ifrow.InterfaceLuid= row->InterfaceLuid;
  38.         ifrow.InterfaceIndex= row->InterfaceIndex;
  39. if(GetIpInterfaceEntry(&ifrow)!= NO_ERROR){
  40. return(-1);
  41. }
  42.         metric = ifrow.Metric+ row->Metric;
  43. if(metric < INT_MAX)
  44.             entry.metric = metric;
  45. else
  46.             entry.metric = INT_MAX;
  47. if((ret =(*callback)(&entry, arg))!=0)
  48. break;
  49. }
  50.     intf_close(intf);
  51. return ret;
  52. }

4.3 路由条目添加与删除

4.3.1 添加路由

  1. // route-win32.c:49-76
  2. int
  3. route_add(route_t*route,conststruct route_entry *entry)
  4. {
  5.     MIB_IPFORWARDROW ipfrow;
  6. struct addr net;
  7.     memset(&ipfrow,0,sizeof(ipfrow));
  8. // 查找最佳接口
  9. if(GetBestInterface(entry->route_gw.addr_ip,
  10. &ipfrow.dwForwardIfIndex)!= NO_ERROR)
  11. return(-1);
  12. // 计算网络地址
  13. if(addr_net(&entry->route_dst,&net)<0||
  14.         net.addr_type != ADDR_TYPE_IP)
  15. return(-1);
  16.     ipfrow.dwForwardDest = net.addr_ip;
  17.     addr_btom(entry->route_dst.addr_bits,
  18. &ipfrow.dwForwardMask, IP_ADDR_LEN);
  19.     ipfrow.dwForwardNextHop = entry->route_gw.addr_ip;
  20.     ipfrow.dwForwardType =4;// 下一条不是最终目的地
  21.     ipfrow.dwForwardProto =3;// MIB_PROTO_NETMGMT
  22. if(CreateIpForwardEntry(&ipfrow)!= NO_ERROR)
  23. return(-1);
  24. return(0);
  25. }

4.3.2 删除路由

  1. // route-win32.c:78-101
  2. int
  3. route_delete(route_t*route,conststruct route_entry *entry)
  4. {
  5.     MIB_IPFORWARDROW ipfrow;
  6.     DWORD mask;
  7. if(entry->route_dst.addr_type != ADDR_TYPE_IP ||
  8. GetBestRoute(entry->route_dst.addr_ip,
  9.         IP_ADDR_ANY,&ipfrow)!= NO_ERROR)
  10. return(-1);
  11.     addr_btom(entry->route_dst.addr_bits,&mask, IP_ADDR_LEN);
  12. // 验证路由条目匹配
  13. if(ipfrow.dwForwardDest != entry->route_dst.addr_ip ||
  14.         ipfrow.dwForwardMask != mask){
  15.         errno = ENXIO;
  16. SetLastError(ERROR_NO_DATA);
  17. return(-1);
  18. }
  19. if(DeleteIpForwardEntry(&ipfrow)!= NO_ERROR)
  20. return(-1);
  21. return(0);
  22. }

ARP模块

5.1 ARP表操作完整实现

  1. // arp-win32.c:96-132
  2. int
  3. arp_loop(arp_t*arp, arp_handler callback,void*arg)
  4. {
  5. struct arp_entry entry;
  6.     ULONG len;
  7. int i, ret;
  8. // 动态调整缓冲区大小
  9. for(len =sizeof(arp->iptable[0]);;){
  10. if(arp->iptable)
  11.             free(arp->iptable);
  12.         arp->iptable = malloc(len);
  13. if(arp->iptable == NULL)
  14. return(-1);
  15.         ret =GetIpNetTable(arp->iptable,&len, FALSE);
  16. if(ret == NO_ERROR)
  17. break;
  18. elseif(ret != ERROR_INSUFFICIENT_BUFFER)
  19. return(-1);
  20. }
  21.     entry.arp_pa.addr_type = ADDR_TYPE_IP;
  22.     entry.arp_pa.addr_bits = IP_ADDR_BITS;
  23.     entry.arp_ha.addr_type = ADDR_TYPE_ETH;
  24.     entry.arp_ha.addr_bits = ETH_ADDR_BITS;
  25. for(=0; i <(int)arp->iptable->dwNumEntries; i++){
  26. // 跳过非以太网条目
  27. if(arp->iptable->table[i].dwPhysAddrLen != ETH_ADDR_LEN)
  28. continue;
  29.         entry.arp_pa.addr_ip = arp->iptable->table[i].dwAddr;
  30.         memcpy(&entry.arp_ha.addr_eth,
  31.             arp->iptable->table[i].bPhysAddr, ETH_ADDR_LEN);
  32. if((ret =(*callback)(&entry, arg))!=0)
  33. return(ret);
  34. }
  35. return(0);
  36. }

5.2 添加静态ARP条目

  1. // arp-win32.c:30-50
  2. int
  3. arp_add(arp_t*arp,conststruct arp_entry *entry)
  4. {
  5.     MIB_IPFORWARDROW ipfrow;
  6.     MIB_IPNETROW iprow;
  7. // 查找出接口
  8. if(GetBestRoute(entry->arp_pa.addr_ip,
  9.         IP_ADDR_ANY,&ipfrow)!= NO_ERROR)
  10. return(-1);
  11.     iprow.dwIndex = ipfrow.dwForwardIfIndex;
  12.     iprow.dwPhysAddrLen = ETH_ADDR_LEN;
  13.     memcpy(iprow.bPhysAddr,&entry->arp_ha.addr_eth, ETH_ADDR_LEN);
  14.     iprow.dwAddr = entry->arp_pa.addr_ip;
  15.     iprow.dwType =4;// 静态条目
  16. if(CreateIpNetEntry(&iprow)!= NO_ERROR)
  17. return(-1);
  18. return(0);
  19. }

5.3 删除ARP条目

  1. // arp-win32.c:52-71
  2. int
  3. arp_delete(arp_t*arp,conststruct arp_entry *entry)
  4. {
  5.     MIB_IPFORWARDROW ipfrow;
  6.     MIB_IPNETROW iprow;
  7. if(GetBestRoute(entry->arp_pa.addr_ip,
  8.         IP_ADDR_ANY,&ipfrow)!= NO_ERROR)
  9. return(-1);
  10.     memset(&iprow,0,sizeof(iprow));
  11.     iprow.dwIndex = ipfrow.dwForwardIfIndex;
  12.     iprow.dwAddr = entry->arp_pa.addr_ip;
  13. if(DeleteIpNetEntry(&iprow)!= NO_ERROR){
  14.         errno = ENXIO;
  15. return(-1);
  16. }
  17. return(0);
  18. }

防火墙模块

6.1 PktFilter驱动通信

Windows防火墙模块通过命名管道与第三方驱动通信:

  1. // fw-pktfilter.c:277-313
  2. staticchar*
  3. call_pipe(constchar*msg,int len)
  4. {
  5.     HANDLE *pipe;
  6.     DWORD i;
  7. char*reply, status;
  8. // 等待命名管道
  9. if(!WaitNamedPipe(PKTFILTER_PIPE, NMPWAIT_USE_DEFAULT_WAIT)||
  10. (pipe =CreateFile(PKTFILTER_PIPE, GENERIC_READ | GENERIC_WRITE,
  11. 0, NULL, OPEN_EXISTING,0, NULL))== INVALID_HANDLE_VALUE){
  12. return(NULL);
  13. }
  14.     reply = NULL;
  15. // 发送消息
  16. if(WriteFile(pipe, msg, len,&i, NULL)){
  17. // 读取响应
  18. if(ReadFile(pipe,&status,sizeof(status),&i, NULL)){
  19. if(status == FILTER_FAILURE){
  20. ReadFile(pipe,&status,sizeof(status),&i, NULL);
  21. }elseif(status == FILTER_MESSAGE){
  22. // 获取消息长度
  23. if(ReadFile(pipe,&len,4,&i, NULL)){
  24. // 获取消息
  25.                     reply = calloc(1, len +1);
  26. if(!ReadFile(pipe, reply, len,&i, NULL)){
  27.                         free(reply);
  28.                         reply = NULL;
  29. }
  30. }
  31. }elseif(status == FILTER_SUCCESS)
  32.                 reply = strdup("");/* XXX */
  33. }
  34. }
  35. CloseHandle(pipe);
  36. return(reply);
  37. }

6.2 防火墙规则格式化

  1. // fw-pktfilter.c:214-275
  2. staticint
  3. format_rule(conststruct fw_rule *rule,char*buf,int len)
  4. {
  5. char tmp[128];
  6.     strlcpy(buf,(rule->fw_op == FW_OP_ALLOW)?"pass ":"block ", len);
  7.     strlcat(buf,(rule->fw_dir == FW_DIR_IN)?"in ":"out ", len);
  8.     snprintf(tmp,sizeof(tmp),"on %s ", rule->fw_device);
  9.     strlcat(buf, tmp, len);
  10. if(rule->fw_proto !=0){
  11.         snprintf(tmp,sizeof(tmp),"proto %d ", rule->fw_proto);
  12.         strlcat(buf, tmp, len);
  13. }
  14. // 源地址
  15. if(rule->fw_src.addr_type != ADDR_TYPE_NONE){
  16.         snprintf(tmp,sizeof(tmp),"from %s ",
  17.             addr_ntoa(&rule->fw_src));
  18.         strlcat(buf, tmp, len);
  19. }else
  20.         strlcat(buf,"from any ", len);
  21. // 源端口(TCP/UDP)
  22. if(rule->fw_proto == IP_PROTO_TCP || rule->fw_proto == IP_PROTO_UDP){
  23. if(rule->fw_sport[0]== rule->fw_sport[1])
  24.             snprintf(tmp,sizeof(tmp),"port = %d ",
  25.                 rule->fw_sport[0]);
  26. else
  27.             snprintf(tmp,sizeof(tmp),"port %d >< %d ",
  28.                 rule->fw_sport[0]-1, rule->fw_sport[1]+1);
  29.         strlcat(buf, tmp, len);
  30. }
  31. // 目的地址
  32. if(rule->fw_dst.addr_type != ADDR_TYPE_NONE){
  33.         snprintf(tmp,sizeof(tmp),"to %s ",
  34.             addr_ntoa(&rule->fw_dst));
  35.         strlcat(buf, tmp, len);
  36. }else
  37.         strlcat(buf,"to any ", len);
  38. // 目的端口(TCP/UDP)或ICMP类型/代码
  39. if(rule->fw_proto == IP_PROTO_TCP || rule->fw_proto == IP_PROTO_UDP){
  40. if(rule->fw_dport[0]== rule->fw_dport[1])
  41.             snprintf(tmp,sizeof(tmp),"port = %d",
  42.                 rule->fw_dport[0]);
  43. else
  44.             snprintf(tmp,sizeof(tmp),"port %d >< %d",
  45.                 rule->fw_dport[0]-1, rule->fw_dport[1]+1);
  46.         strlcat(buf, tmp, len);
  47. }elseif(rule->fw_proto == IP_PROTO_ICMP){
  48. if(rule->fw_sport[1]){
  49.             snprintf(tmp,sizeof(tmp),"icmp-type %d",
  50.                 rule->fw_sport[0]);
  51.             strlcat(buf, tmp, len);
  52. if(rule->fw_dport[1]){
  53.                 snprintf(tmp,sizeof(tmp)," code %d",
  54.                     rule->fw_dport[0]);
  55.                 strlcat(buf, tmp, len);
  56. }
  57. }
  58. }
  59. return(strlen(buf));
  60. }

以太网模块限制

7.1 Windows以太网存根实现

  1. // eth-win32.c:20-51
  2. eth_t*
  3. eth_open(constchar*device)
  4. {
  5. return(NULL);// Windows原生不支持
  6. }
  7. ssize_t
  8. eth_send(eth_t*eth,constvoid*buf,size_t len)
  9. {
  10. return(-1);
  11. }
  12. eth_t*
  13. eth_close(eth_t*eth)
  14. {
  15. return(NULL);
  16. }
  17. int
  18. eth_get(eth_t*eth,eth_addr_t*ea)
  19. {
  20. return(-1);
  21. }
  22. int
  23. eth_set(eth_t*eth,consteth_addr_t*ea)
  24. {
  25. return(-1);
  26. }

原因:Windows内核不提供直接访问以太网层的API

7.2 变通方案

方案1:使用WinPcap/Npcap

  1. // 需要安装WinPcap或Npcap
  2. #include<pcap.h>
  3. eth_t*eth_open_winpcap(constchar*device){
  4. char errbuf[PCAP_ERRBUF_SIZE];
  5. pcap_t*pcap = pcap_open_live(device,65535,1,1000, errbuf);
  6. return(eth_t*)pcap;
  7. }

方案2:原始套接字(有限功能)

  1. // 只能发送IP层及以上的数据包
  2. SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  3. setsockopt(sock, IPPROTO_IP, IP_HDRINCL,&on,sizeof(on));

Windows平台限制详解

8.1 权限限制

8.1.1 原始套接字限制

Windows版本
原始套接字支持
要求
Windows XP
完整支持
管理员权限
Windows Vista/7
有限支持
管理员权限,某些功能受限
Windows 8/10/11
严格限制
仅允许发送,禁止接收

代码示例

  1. // ip-win32.c:40-43
  2. if(setsockopt(ip->fd, IPPROTO_IP, IP_HDRINCL,
  3. (constchar*)&on,sizeof(on))== SOCKET_ERROR){
  4. SetLastError(ERROR_NETWORK_ACCESS_DENIED);// 权限不足
  5. return(ip_close(ip));
  6. }

8.1.2 防火墙限制

Windows防火墙默认阻止原始套接字操作,需要:

  1. 以管理员身份运行
  2. 配置防火墙规则
  3. 部分操作需要完全禁用防火墙(不推荐)

8.2 功能限制

8.2.1 缺失功能对比

功能
Linux
BSD/macOS
Windows
说明
以太网帧发送
需要WinPcap/Npcap
以太网帧接收
需要WinPcap/Npcap
原始IP发送
需要管理员权限
原始IP接收
⚠️
Windows 8+受限
路由表读取
使用IP Helper API
路由表修改
需要管理员权限
ARP表读取
使用IP Helper API
ARP缓存操作
需要管理员权限
防火墙规则
⚠️
需要第三方驱动
接口枚举
使用IP Helper API

8.2.2 IPv6支持差异

  • Linux/BSD:原生支持IPv6的所有功能
  • Windows:仅部分API支持IPv6(如 GetIpForwardTable2

8.3 性能限制

8.3.1 API调用开销

Windows IP Helper API通过系统调用访问内核数据,比Linux的直接 /proc文件访问更慢。

性能对比

  1. Linux(读取/proc/net/route):~100μs
  2. Windows(GetIpForwardTable):~500μ-1ms

8.3.2 内存分配模式

  1. // Windows:每次调用需要重新分配缓冲区
  2. for(len =sizeof(table[0]);;){
  3.     free(table);
  4.     table = malloc(len);
  5.     ret =GetIpForwardTable(table,&len, FALSE);
  6. if(ret == NO_ERROR)break;
  7. }
  8. // Linux:可以直接读取固定大小的文件
  9. FILE*fp = fopen("/proc/net/route","r");
  10. while(fgets(line,sizeof(line), fp)){
  11. // 解析行
  12. }

8.4 安全限制

8.4.1 UAC (用户账户控制)

  • 提示用户提升权限时可能被拒绝
  • 某些环境下完全禁用UAC以使用libdnet

8.4.2 反病毒软件

原始套接字操作可能被标记为可疑行为:

  • 关闭实时扫描
  • 添加白名单

8.4.3 企业策略

企业环境可能:

  • 禁用原始套接字
  • 强制启用防火墙
  • 禁止未签名驱动

各平台实现对比

9.1 网络接口管理对比

特性
Linux (intf-linux.c)
BSD (intf-bsd.c)
Windows (intf-win32.c)
数据源
/proc/net/if, ioctl
sysctl, ioctl
GetAdaptersAddresses
IPv4/IPv6
MAC地址
MTU
接口标志
动态更新
⚠️ (需重新调用)
代码复杂度

9.2 IP模块对比

特性
Linux (ip.c)
BSD (ip.c)
Windows (ip-win32.c)
套接字类型
socket(AFINET, SOCKRAW)
socket(AFINET, SOCKRAW)
socket(AFINET, SOCKRAW)
IP头包含
setsockopt(IP_HDRINCL)
setsockopt(IP_HDRINCL)
setsockopt(IP_HDRINCL)
发送
sendto()
sendto()
sendto()
接收
recv()
recv()
❌ (Windows 8+)
权限要求
CAPNETRAW
root
管理员

9.3 路由模块对比

特性
Linux (route-linux.c)
BSD (route-bsd.c)
Windows (route-win32.c)
数据源
Netlink套接字
路由套接字
GetIpForwardTable/2
IPv4
IPv6
✅ (仅Vista+)
动态更新
⚠️ (需重新调用)
修改权限
CAPNETADMIN
root
管理员
接口查询

9.4 ARP模块对比

特性
Linux (arp-linux.c)
BSD (arp-bsd.c)
Windows (arp-win32.c)
数据源
/proc/net/arp
sysctl
GetIpNetTable
静态条目
动态更新
⚠️ (需重新调用)
修改权限
CAPNETADMIN
root
管理员

9.5 防火墙模块对比

特性
Linux (fw-ipchains/ipfw)
BSD (fw-ipfw/pf/ipf)
Windows (fw-pktfilter)
实现方式
setsockopt
ioctl/sysctl
命名管道
IPv4
⚠️ (需第三方驱动)
IPv6
⚠️
状态跟踪
⚠️
⚠️
驱动依赖
内置
内置
需要安装PktFilter

变通方案与最佳实践

10.1 以太网功能实现

方案1:WinPcap/Npcap(推荐)

  1. #include<pcap.h>
  2. typedefstruct{
  3. pcap_t*pcap;
  4. char device[256];
  5. }eth_winpcap_t;
  6. eth_winpcap_t*eth_open_winpcap(constchar*device){
  7. eth_winpcap_t*= calloc(1,sizeof(*e));
  8. char errbuf[PCAP_ERRBUF_SIZE];
  9.     e->pcap = pcap_open_live(device,65535,1,1000, errbuf);
  10. if(e->pcap == NULL){
  11.         free(e);
  12. return NULL;
  13. }
  14.     strncpy(e->device, device,sizeof(e->device)-1);
  15. return e;
  16. }
  17. int eth_send_winpcap(eth_winpcap_t*e,const u_char *buf,int len){
  18. return pcap_sendpacket(e->pcap, buf, len);
  19. }
  20. void eth_close_winpcap(eth_winpcap_t*e){
  21. if(e){
  22.         pcap_close(e->pcap);
  23.         free(e);
  24. }
  25. }

方案2:NDIS用户模式驱动

开发自定义NDIS驱动(复杂,不推荐用于普通应用)

10.2 权限提升方案

自动请求管理员权限

  1. #include<shellapi.h>
  2. int restart_as_admin(){
  3.     SHELLEXECUTEINFO sei ={0};
  4.     sei.cbSize =sizeof(sei);
  5.     sei.lpVerb ="runas";// 请求管理员权限
  6.     sei.lpFile =GetCommandLine();
  7.     sei.hwnd = NULL;
  8.     sei.nShow = SW_NORMAL;
  9. if(!ShellExecuteEx(&sei)){
  10.         DWORD dwError =GetLastError();
  11. if(dwError == ERROR_CANCELLED){
  12.             printf("用户取消了权限提升请求\n");
  13. return-1;
  14. }
  15. }
  16. return0;
  17. }

运行时权限检查

  1. int is_admin(){
  2.     BOOL isAdmin = FALSE;
  3.     PSID adminGroup = NULL;
  4. // 获取管理员组SID
  5.     SID_IDENTIFIER_AUTHORITY auth = SECURITY_NT_AUTHORITY;
  6. if(AllocateAndInitializeSid(&auth,2, SECURITY_BUILTIN_DOMAIN_RID,
  7.         DOMAIN_ALIAS_RID_ADMINS,0,0,0,0,0,0,&adminGroup)){
  8. if(!CheckTokenMembership(NULL, adminGroup,&isAdmin)){
  9.             isAdmin = FALSE;
  10. }
  11. FreeSid(adminGroup);
  12. }
  13. return isAdmin;
  14. }

10.3 性能优化建议

10.3.1 缓存接口信息

  1. typedefstruct{
  2.     IP_ADAPTER_ADDRESSES *iftable;
  3. time_t last_update;
  4. time_t cache_timeout;
  5. }intf_cache_t;
  6. intf_cache_t*intf_cache_create(int timeout){
  7. intf_cache_t*cache = calloc(1,sizeof(*cache));
  8.     cache->cache_timeout = timeout;
  9. return cache;
  10. }
  11. int intf_cache_refresh(intf_cache_t*cache){
  12. time_t now = time(NULL);
  13. // 检查是否需要刷新
  14. if(cache->iftable &&(now - cache->last_update)< cache->cache_timeout){
  15. return0;// 使用缓存
  16. }
  17. // 刷新缓存
  18. if(cache->iftable) free(cache->iftable);
  19.     ULONG len =16384;
  20.     IP_ADAPTER_ADDRESSES *p;
  21. do{
  22.         free(p);
  23.         p = malloc(len);
  24. if(GetAdaptersAddresses(AF_UNSPEC,
  25.             GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST,
  26.             NULL, p,&len)!= ERROR_BUFFER_OVERFLOW){
  27. break;
  28. }
  29. }while(1);
  30.     cache->iftable = p;
  31.     cache->last_update = now;
  32. return0;
  33. }

10.3.2 批量操作优化

  1. // 批量添加路由
  2. int route_batch_add(route_t*r,struct route_entry *entries,int count){
  3. int i, failed =0;
  4. for(=0; i < count; i++){
  5. if(route_add(r,&entries[i])<0){
  6.             failed++;
  7. }
  8. }
  9. return failed;
  10. }

10.4 错误处理增强

  1. #include<stdio.h>
  2. void print_winsock_error(){
  3. int err =WSAGetLastError();
  4. char*msg = NULL;
  5. FormatMessageA(
  6.         FORMAT_MESSAGE_ALLOCATE_BUFFER |
  7.         FORMAT_MESSAGE_FROM_SYSTEM |
  8.         FORMAT_MESSAGE_IGNORE_INSERTS,
  9.         NULL,
  10.         err,
  11.         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  12. (LPSTR)&msg,
  13. 0,
  14.         NULL
  15. );
  16. if(msg){
  17.         fprintf(stderr,"Winsock错误 %d: %s\n", err, msg);
  18. LocalFree(msg);
  19. }else{
  20.         fprintf(stderr,"Winsock错误 %d: 未知错误\n", err);
  21. }
  22. }
  23. void print_iphlpapi_error(DWORD err){
  24. char*msg = NULL;
  25. FormatMessageA(
  26.         FORMAT_MESSAGE_ALLOCATE_BUFFER |
  27.         FORMAT_MESSAGE_FROM_SYSTEM |
  28.         FORMAT_MESSAGE_IGNORE_INSERTS,
  29.         NULL,
  30.         err,
  31.         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  32. (LPSTR)&msg,
  33. 0,
  34.         NULL
  35. );
  36. if(msg){
  37.         fprintf(stderr,"IP Helper API错误 %d: %s\n", err, msg);
  38. LocalFree(msg);
  39. }else{
  40.         fprintf(stderr,"IP Helper API错误 %d: 未知错误\n", err);
  41. }
  42. }

附录:完整代码示例

A.1 Windows平台网络工具完整示例

  1. /*
  2.  * libdnet Windows网络工具完整示例
  3.  * 功能:枚举接口、路由表、ARP表
  4.  */
  5. #include<stdio.h>
  6. #include<winsock2.h>
  7. #include"dnet.h"
  8. // 打印接口信息
  9. staticint print_interface(conststruct intf_entry *entry,void*arg){
  10.     printf("\n[%s]\n", entry->intf_name);
  11.     printf("  Flags: 0x%04X", entry->intf_flags);
  12. if(entry->intf_flags & INTF_FLAG_UP) printf(" UP");
  13. if(entry->intf_flags & INTF_FLAG_LOOPBACK) printf(" LOOPBACK");
  14. if(entry->intf_flags & INTF_FLAG_MULTICAST) printf(" MULTICAST");
  15.     printf("\n");
  16. if(entry->intf_mtu !=0)
  17.         printf("  MTU: %d\n", entry->intf_mtu);
  18. if(entry->intf_addr.addr_type == ADDR_TYPE_IP)
  19.         printf("  IP: %s/%d\n", addr_ntoa(&entry->intf_addr), entry->intf_addr.addr_bits);
  20. if(entry->intf_link_addr.addr_type == ADDR_TYPE_ETH)
  21.         printf("  MAC: %s\n", addr_ntoa(&entry->intf_link_addr));
  22. return0;
  23. }
  24. // 打印路由表
  25. staticint print_route(conststruct route_entry *entry,void*arg){
  26.     printf("%-18s %-18s %-10s %5d\n",
  27.         addr_ntoa(&entry->route_dst),
  28.         addr_ntoa(&entry->route_gw),
  29.         entry->intf_name,
  30.         entry->metric);
  31. return0;
  32. }
  33. // 打印ARP表
  34. staticint print_arp(conststruct arp_entry *entry,void*arg){
  35.     printf("%-18s %-18s\n",
  36.         addr_ntoa(&entry->arp_pa),
  37.         addr_ntoa(&entry->arp_ha));
  38. return0;
  39. }
  40. int main(void){
  41.     WSADATA wsaData;
  42. intf_t*intf;
  43. route_t*route;
  44. arp_t*arp;
  45. // 初始化Winsock
  46. if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0){
  47.         fprintf(stderr,"WSAStartup失败\n");
  48. return1;
  49. }
  50.     printf("=== libdnet Windows网络工具 ===\n");
  51. // 枚举接口
  52.     printf("\n1. 网络接口:\n");
  53. if((intf = intf_open())!= NULL){
  54.         intf_loop(intf, print_interface, NULL);
  55.         intf_close(intf);
  56. }
  57. // 枚举路由表
  58.     printf("\n2. 路由表:\n");
  59.     printf("%-18s %-18s %-10s %5s\n","目标","网关","接口","度量");
  60.     printf("------------------ ------------------ ---------- -----\n");
  61. if((route = route_open())!= NULL){
  62.         route_loop(route, print_route, NULL);
  63.         route_close(route);
  64. }
  65. // 枚举ARP表
  66.     printf("\n3. ARP表:\n");
  67.     printf("%-18s %-18s\n","IP地址","MAC地址");
  68.     printf("------------------ ------------------\n");
  69. if((arp = arp_open())!= NULL){
  70.         arp_loop(arp, print_arp, NULL);
  71.         arp_close(arp);
  72. }
  73. WSACleanup();
  74. return0;
  75. }

A.2 跨平台抽象层

  1. /*
  2.  * 平台特定头文件
  3.  */
  4. #ifdef _WIN32
  5. #include<winsock2.h>
  6. #include<windows.h>
  7. #include<iphlpapi.h>
  8. #pragma comment(lib,"ws2_32.lib")
  9. #pragma comment(lib,"iphlpapi.lib")
  10. #else
  11. #include<sys/socket.h>
  12. #include<netinet/in.h>
  13. #include<arpa/inet.h>
  14. #include<unistd.h>
  15. #endif
  16. /*
  17.  * 平台特定宏
  18.  */
  19. #ifdef _WIN32
  20. #define SOCKET_INVALID INVALID_SOCKET
  21. #define socket_close closesocket
  22. #define socket_errno WSAGetLastError()
  23. #define sleep_ms(ms)Sleep(ms)
  24. #else
  25. #define SOCKET_INVALID -1
  26. #define socket_close close
  27. #define socket_errno errno
  28. #define sleep_ms(ms) usleep((ms)*1000)
  29. #endif
  30. /*
  31.  * 初始化函数
  32.  */
  33. #ifdef _WIN32
  34. int network_init(void){
  35.     WSADATA wsaData;
  36. returnWSAStartup(MAKEWORD(2,2),&wsaData);
  37. }
  38. void network_cleanup(void){
  39. WSACleanup();
  40. }
  41. #else
  42. int network_init(void){
  43. return0;// Linux/macOS无需初始化
  44. }
  45. void network_cleanup(void){
  46. // 无需清理
  47. }
  48. #endif

总结

Windows平台关键限制

  1. 以太网层访问:完全不支持,必须依赖WinPcap/Npcap
  2. 原始套接字接收:Windows 8+严格限制
  3. 权限要求:大部分功能需要管理员权限
  4. 第三方防火墙:需要安装额外驱动
  5. API性能:比Linux直接访问 /proc

设计哲学

libdnet在Windows上采用零依赖的设计理念:

  • 完全使用Windows原生API
  • 通过IP Helper API实现大部分功能
  • 对于不可实现的功能(如以太网),提供存根实现
  • 通过动态加载实现版本兼容

适用场景

场景
推荐方案
简单网络工具
libdnet原生实现
需要以太网功能
libdnet + WinPcap/Npcap
高性能包捕获
直接使用WinPcap/Npcap
防火墙管理
使用Windows防火墙API

最佳实践

  1. 权限检查:运行时检查并提示用户提升权限
  2. 错误处理:使用 GetLastError()和 FormatMessage()提供详细错误信息
  3. 版本兼容:使用动态加载支持旧版本Windows
  4. 缓存机制:缓存接口信息减少API调用开销
  5. Unicode支持:正确处理宽字符与多字节字符转换
  • 公众号:安全狗的自我修养

  • vx:2207344074

  • http://gitee.com/haidragon

  • http://github.com/haidragon

  • bilibili:haidragonx