跨平台底层网络库libdnet源码分析系列(十三)
源码分析mettle后门工具学习 所使用的依赖库

Windows内核分支深度分析与限制分析
目录
- Windows平台架构概述
- 内核分支实现深度分析
- 网络接口管理模块
- IP与路由模块
- ARP模块
- 防火墙模块
- 以太网模块限制
- Windows平台限制详解
- 各平台实现对比
- 变通方案与最佳实践
- 附录:完整代码示例
Windows平台架构概述
1.1 平台选择机制
libdnet通过编译时条件编译选择特定平台的实现:
1.2 Windows特定文件清单
|
|
|
|
|---|---|---|
|
|
intf-win32.c |
intf-bsd.c
intf-linux.c |
|
|
ip-win32.c |
ip.c
|
|
|
route-win32.c |
route-bsd.c
route-linux.c |
|
|
arp-win32.c |
arp-bsd.c
arp-linux.c |
|
|
fw-pktfilter.c |
fw-ipfw.c
fw-pf.c, fw-ipf.c, fw-ipchains.c |
|
|
eth-win32.c
|
eth-bsd.c
eth-linux.c, eth-dlpi.c |
1.3 核心设计理念
- 零依赖设计:不依赖WinPcap/Npcap,使用原生Windows API
- 版本兼容性:通过动态加载支持Windows XP到Windows 11
- 权限要求:部分功能需要管理员权限(原始套接字、路由表修改)
- Unicode处理:正确处理宽字符(
wchar_t)与多字节字符的转换
内核分支实现深度分析
2.1 Windows内核网络架构
libdnet在Windows上的实现直接利用Windows内核提供的网络API,架构层次如下:
┌─────────────────────────────────────────┐│ libdnet用户态API │├─────────────────────────────────────────┤│WindowsSockets2(Winsock2)│├─────────────────────────────────────────┤│ IP Helper API (iphlpapi.dll)││-GetAdaptersAddresses││-GetIpForwardTable/2││-GetIpNetTable│├─────────────────────────────────────────┤│Windows网络驱动(ndis.sys)│├─────────────────────────────────────────┤│网络接口卡驱动│└─────────────────────────────────────────┘
2.2 数据结构映射
2.2.1 网络接口结构
// intf-win32.cstruct intf_handle {struct ifcombo ifcombo[MIB_IF_TYPE_MAX];// 按类型分组的接口索引IP_ADAPTER_ADDRESSES *iftable;// Windows接口表};struct ifcombo {struct{DWORD ipv4;// IPv4接口索引DWORD ipv6;// IPv6接口索引}*idx;int cnt;// 数量int max;// 容量};
关键设计点:
- 将Windows的
IP_ADAPTER_ADDRESSES结构映射到libdnet的intf_entry - 使用
ifcombo数组按接口类型(以太网、PPP、环回等)分组 - 支持IPv4和IPv6双栈
2.2.2 路由表结构
// route-win32.cstruct route_handle {HINSTANCE iphlpapi;// iphlpapi.dll模块句柄MIB_IPFORWARDTABLE *ipftable;// IPv4路由表(XP/2003)MIB_IPFORWARD_TABLE2 *ipftable2;// IPv4/IPv6路由表(Vista+)};
版本兼容策略:
- 动态加载
GetIpForwardTable2(仅Vista+可用) - 保留旧版
GetIpForwardTable以支持XP/2003
网络接口管理模块
3.1 GetAdaptersAddresses深度解析
3.1.1 完整实现代码
// intf-win32.c:220-268staticint_refresh_tables(intf_t*intf){IP_ADAPTER_ADDRESSES *p;DWORD ret;ULONG len;p = NULL;len =16384;// 初始大缓冲区,避免Windows 2003的bugdo{free(p);p = malloc(len);if(p == NULL)return(-1);ret =GetAdaptersAddresses(AF_UNSPEC,// IPv4和IPv6GAA_FLAG_INCLUDE_PREFIX |// 包含前缀信息GAA_FLAG_SKIP_ANYCAST |GAA_FLAG_SKIP_MULTICAST,NULL,p,&len);}while(ret == ERROR_BUFFER_OVERFLOW);if(ret != NO_ERROR){free(p);return(-1);}intf->iftable = p;// 映射Windows索引到libdnet索引for(p = intf->iftable; p != NULL; p = p->Next){int type;type = _if_type_canonicalize(p->IfType);if(type < MIB_IF_TYPE_MAX)_ifcombo_add(&intf->ifcombo[type], p->IfIndex, p->Ipv6IfIndex);elsereturn(-1);}return(0);}
3.1.2 Windows 2003缓冲区Bug处理
问题描述: 在Windows 2003上, GetAdaptersAddresses的行为异常:
- 首次调用缓冲区不足:返回
ERROR_BUFFER_OVERFLOW - 后续调用缓冲区不足:返回
ERROR_INVALID_PARAMETER
解决方案:
// 首次调用使用大缓冲区(16KB)len =16384;do{// ...}while(ret == ERROR_BUFFER_OVERFLOW);
3.1.3 宽字符转换处理
// intf-win32.c:40-48staticchar* strncpy_wchar(char*dst, PWCHAR src,size_t n){size_t wlen = wcslen(src)+2;char*tmp = calloc(1, wlen);WideCharToMultiByte(CP_ACP,0, src,-1, tmp, wlen, NULL, NULL);strncpy(dst, tmp, n);free(tmp);return dst;}
调用示例:
// 转换友好名称strncpy_wchar(friendly_name, a->FriendlyName, MAX_INTERFACE_NAME_LEN);// 转换驱动描述strncpy_wchar(entry->driver_name, a->Description,sizeof(entry->driver_name));
3.2 接口类型标准化
3.2.1 Windows MIB类型到libdnet类型的映射
// intf-win32.c:50-72staticchar*_ifcombo_name(int type){char*name ="eth";// 默认以太网if(type == IF_TYPE_ISO88025_TOKENRING){name ="tr";}elseif(type == IF_TYPE_PPP){name ="ppp";}elseif(type == IF_TYPE_SOFTWARE_LOOPBACK){name ="lo";}elseif(type == IF_TYPE_TUNNEL){name ="tun";}return(name);}
3.2.2 接口标志映射
// intf-win32.c:164-172entry->intf_flags =0;if(a->OperStatus==IfOperStatusUp)entry->intf_flags |= INTF_FLAG_UP;if(a->IfType== IF_TYPE_SOFTWARE_LOOPBACK)entry->intf_flags |= INTF_FLAG_LOOPBACK;elseentry->intf_flags |= INTF_FLAG_MULTICAST;
3.3 前缀长度查找
Windows XP/Vista前缀信息存储方式不同:
// intf-win32.c:196-202bits =0;for(prefix = a->FirstPrefix; prefix != NULL; prefix = prefix->Next){if(prefix->Address.lpSockaddr->sa_family == addr->Address.lpSockaddr->sa_family){bits =(unsignedshort) prefix->PrefixLength;break;}}
Vista及以后:使用 OnLinkPrefixLength成员
IP与路由模块
4.1 IP模块:原始套接字实现
4.1.1 完整源码分析
// ip-win32.c:24-49ip_t*ip_open(void){BOOL on;ip_t*ip;if((ip = calloc(1,sizeof(*ip)))!= NULL){// 初始化Winsockif(WSAStartup(MAKEWORD(2,2),&ip->wsdata)!=0){free(ip);return(NULL);}// 创建原始套接字if((ip->fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==INVALID_SOCKET)return(ip_close(ip));// 设置包含IP头选项on = TRUE;if(setsockopt(ip->fd, IPPROTO_IP, IP_HDRINCL,(constchar*)&on,sizeof(on))== SOCKET_ERROR){SetLastError(ERROR_NETWORK_ACCESS_DENIED);return(ip_close(ip));}ip->sin.sin_family = AF_INET;ip->sin.sin_port = htons(666);}return(ip);}
4.1.2 IP头包含选项
// 设置IP_HDRINCL允许应用构建完整IP头setsockopt(ip->fd, IPPROTO_IP, IP_HDRINCL,&on,sizeof(on));
作用:应用层提供完整IP头,系统不再自动添加
4.1.3 数据包发送
// ip-win32.c:51-63ssize_tip_send(ip_t*ip,constvoid*buf,size_t len){struct ip_hdr *hdr =(struct ip_hdr *)buf;ip->sin.sin_addr.s_addr = hdr->ip_src;if((len = sendto(ip->fd,(constchar*)buf, len,0,(struct sockaddr *)&ip->sin,sizeof(ip->sin)))!= SOCKET_ERROR)return(len);return(-1);}
4.2 路由模块:动态API加载
4.2.1 模块初始化
// route-win32.c:36-47route_t*route_open(void){route_t*r;r = calloc(1,sizeof(route_t));if(r == NULL)return NULL;// 获取iphlpapi.dll模块句柄r->iphlpapi =GetModuleHandle("iphlpapi.dll");return r;}
4.2.2 版本兼容的路由表遍历
// route-win32.c:256-270introute_loop(route_t*r, route_handler callback,void*arg){GETIPFORWARDTABLE2 GetIpForwardTable2;// 动态加载GetIpForwardTable2(仅Vista+)GetIpForwardTable2= NULL;if(r->iphlpapi != NULL)GetIpForwardTable2=(GETIPFORWARDTABLE2)GetProcAddress(r->iphlpapi,"GetIpForwardTable2");if(GetIpForwardTable2== NULL)return route_loop_getipforwardtable(r, callback, arg);// XP/2003elsereturn route_loop_getipforwardtable2(GetIpForwardTable2, r, callback, arg);// Vista+}
4.2.3 IPv4路由表读取(XP/2003)
// route-win32.c:142-197staticintroute_loop_getipforwardtable(route_t*r, route_handler callback,void*arg){struct route_entry entry;intf_t*intf;ULONG len;int i, ret;// 动态调整缓冲区大小for(len =sizeof(r->ipftable[0]);;){if(r->ipftable)free(r->ipftable);r->ipftable = malloc(len);if(r->ipftable == NULL)return(-1);ret =GetIpForwardTable(r->ipftable,&len, FALSE);if(ret == NO_ERROR)break;elseif(ret != ERROR_INSUFFICIENT_BUFFER)return(-1);}intf = intf_open();ret =0;for(i =0; i <(int)r->ipftable->dwNumEntries; i++){// 填充路由条目entry.route_dst.addr_type = ADDR_TYPE_IP;entry.route_dst.addr_bits = IP_ADDR_BITS;entry.route_gw.addr_type = ADDR_TYPE_IP;entry.route_gw.addr_bits = IP_ADDR_BITS;entry.route_dst.addr_ip = r->ipftable->table[i].dwForwardDest;addr_mtob(&r->ipftable->table[i].dwForwardMask, IP_ADDR_LEN,&entry.route_dst.addr_bits);entry.route_gw.addr_ip = r->ipftable->table[i].dwForwardNextHop;entry.metric = r->ipftable->table[i].dwForwardMetric1;// 查找接口名称entry.intf_name[0]='\0';if(intf_get_index(intf,&intf_entry,AF_INET, r->ipftable->table[i].dwForwardIfIndex)==0){strlcpy(entry.intf_name, intf_entry.intf_name,sizeof(entry.intf_name));}if((ret =(*callback)(&entry, arg))!=0)break;}intf_close(intf);return ret;}
4.2.4 IPv4/IPv6路由表读取(Vista+)
// route-win32.c:199-254staticintroute_loop_getipforwardtable2(GETIPFORWARDTABLE2 GetIpForwardTable2,route_t*r, route_handler callback,void*arg){struct route_entry entry;intf_t*intf;ULONG i;int ret;// 获取IPv4和IPv6路由表ret =GetIpForwardTable2(AF_UNSPEC,&r->ipftable2);if(ret != NO_ERROR)return(-1);intf = intf_open();ret =0;for(i =0; i < r->ipftable2->NumEntries; i++){struct intf_entry intf_entry;MIB_IPFORWARD_ROW2 *row;MIB_IPINTERFACE_ROW ifrow;ULONG metric;row =&r->ipftable2->Table[i];// 填充路由条目(支持IPv4和IPv6)addr_ston((struct sockaddr *)&row->DestinationPrefix.Prefix,&entry.route_dst);entry.route_dst.addr_bits = row->DestinationPrefix.PrefixLength;addr_ston((struct sockaddr *)&row->NextHop,&entry.route_gw);// 查找接口名称entry.intf_name[0]='\0';if(intf_get_index(intf,&intf_entry,row->DestinationPrefix.Prefix.si_family,row->InterfaceIndex)==0){strlcpy(entry.intf_name, intf_entry.intf_name,sizeof(entry.intf_name));}// 计算度量值(接口度量 + 路由度量)ifrow.Family= row->DestinationPrefix.Prefix.si_family;ifrow.InterfaceLuid= row->InterfaceLuid;ifrow.InterfaceIndex= row->InterfaceIndex;if(GetIpInterfaceEntry(&ifrow)!= NO_ERROR){return(-1);}metric = ifrow.Metric+ row->Metric;if(metric < INT_MAX)entry.metric = metric;elseentry.metric = INT_MAX;if((ret =(*callback)(&entry, arg))!=0)break;}intf_close(intf);return ret;}
4.3 路由条目添加与删除
4.3.1 添加路由
// route-win32.c:49-76introute_add(route_t*route,conststruct route_entry *entry){MIB_IPFORWARDROW ipfrow;struct addr net;memset(&ipfrow,0,sizeof(ipfrow));// 查找最佳接口if(GetBestInterface(entry->route_gw.addr_ip,&ipfrow.dwForwardIfIndex)!= NO_ERROR)return(-1);// 计算网络地址if(addr_net(&entry->route_dst,&net)<0||net.addr_type != ADDR_TYPE_IP)return(-1);ipfrow.dwForwardDest = net.addr_ip;addr_btom(entry->route_dst.addr_bits,&ipfrow.dwForwardMask, IP_ADDR_LEN);ipfrow.dwForwardNextHop = entry->route_gw.addr_ip;ipfrow.dwForwardType =4;// 下一条不是最终目的地ipfrow.dwForwardProto =3;// MIB_PROTO_NETMGMTif(CreateIpForwardEntry(&ipfrow)!= NO_ERROR)return(-1);return(0);}
4.3.2 删除路由
// route-win32.c:78-101introute_delete(route_t*route,conststruct route_entry *entry){MIB_IPFORWARDROW ipfrow;DWORD mask;if(entry->route_dst.addr_type != ADDR_TYPE_IP ||GetBestRoute(entry->route_dst.addr_ip,IP_ADDR_ANY,&ipfrow)!= NO_ERROR)return(-1);addr_btom(entry->route_dst.addr_bits,&mask, IP_ADDR_LEN);// 验证路由条目匹配if(ipfrow.dwForwardDest != entry->route_dst.addr_ip ||ipfrow.dwForwardMask != mask){errno = ENXIO;SetLastError(ERROR_NO_DATA);return(-1);}if(DeleteIpForwardEntry(&ipfrow)!= NO_ERROR)return(-1);return(0);}
ARP模块
5.1 ARP表操作完整实现
// arp-win32.c:96-132intarp_loop(arp_t*arp, arp_handler callback,void*arg){struct arp_entry entry;ULONG len;int i, ret;// 动态调整缓冲区大小for(len =sizeof(arp->iptable[0]);;){if(arp->iptable)free(arp->iptable);arp->iptable = malloc(len);if(arp->iptable == NULL)return(-1);ret =GetIpNetTable(arp->iptable,&len, FALSE);if(ret == NO_ERROR)break;elseif(ret != ERROR_INSUFFICIENT_BUFFER)return(-1);}entry.arp_pa.addr_type = ADDR_TYPE_IP;entry.arp_pa.addr_bits = IP_ADDR_BITS;entry.arp_ha.addr_type = ADDR_TYPE_ETH;entry.arp_ha.addr_bits = ETH_ADDR_BITS;for(i =0; i <(int)arp->iptable->dwNumEntries; i++){// 跳过非以太网条目if(arp->iptable->table[i].dwPhysAddrLen != ETH_ADDR_LEN)continue;entry.arp_pa.addr_ip = arp->iptable->table[i].dwAddr;memcpy(&entry.arp_ha.addr_eth,arp->iptable->table[i].bPhysAddr, ETH_ADDR_LEN);if((ret =(*callback)(&entry, arg))!=0)return(ret);}return(0);}
5.2 添加静态ARP条目
// arp-win32.c:30-50intarp_add(arp_t*arp,conststruct arp_entry *entry){MIB_IPFORWARDROW ipfrow;MIB_IPNETROW iprow;// 查找出接口if(GetBestRoute(entry->arp_pa.addr_ip,IP_ADDR_ANY,&ipfrow)!= NO_ERROR)return(-1);iprow.dwIndex = ipfrow.dwForwardIfIndex;iprow.dwPhysAddrLen = ETH_ADDR_LEN;memcpy(iprow.bPhysAddr,&entry->arp_ha.addr_eth, ETH_ADDR_LEN);iprow.dwAddr = entry->arp_pa.addr_ip;iprow.dwType =4;// 静态条目if(CreateIpNetEntry(&iprow)!= NO_ERROR)return(-1);return(0);}
5.3 删除ARP条目
// arp-win32.c:52-71intarp_delete(arp_t*arp,conststruct arp_entry *entry){MIB_IPFORWARDROW ipfrow;MIB_IPNETROW iprow;if(GetBestRoute(entry->arp_pa.addr_ip,IP_ADDR_ANY,&ipfrow)!= NO_ERROR)return(-1);memset(&iprow,0,sizeof(iprow));iprow.dwIndex = ipfrow.dwForwardIfIndex;iprow.dwAddr = entry->arp_pa.addr_ip;if(DeleteIpNetEntry(&iprow)!= NO_ERROR){errno = ENXIO;return(-1);}return(0);}
防火墙模块
6.1 PktFilter驱动通信
Windows防火墙模块通过命名管道与第三方驱动通信:
// fw-pktfilter.c:277-313staticchar*call_pipe(constchar*msg,int len){HANDLE *pipe;DWORD i;char*reply, status;// 等待命名管道if(!WaitNamedPipe(PKTFILTER_PIPE, NMPWAIT_USE_DEFAULT_WAIT)||(pipe =CreateFile(PKTFILTER_PIPE, GENERIC_READ | GENERIC_WRITE,0, NULL, OPEN_EXISTING,0, NULL))== INVALID_HANDLE_VALUE){return(NULL);}reply = NULL;// 发送消息if(WriteFile(pipe, msg, len,&i, NULL)){// 读取响应if(ReadFile(pipe,&status,sizeof(status),&i, NULL)){if(status == FILTER_FAILURE){ReadFile(pipe,&status,sizeof(status),&i, NULL);}elseif(status == FILTER_MESSAGE){// 获取消息长度if(ReadFile(pipe,&len,4,&i, NULL)){// 获取消息reply = calloc(1, len +1);if(!ReadFile(pipe, reply, len,&i, NULL)){free(reply);reply = NULL;}}}elseif(status == FILTER_SUCCESS)reply = strdup("");/* XXX */}}CloseHandle(pipe);return(reply);}
6.2 防火墙规则格式化
// fw-pktfilter.c:214-275staticintformat_rule(conststruct fw_rule *rule,char*buf,int len){char tmp[128];strlcpy(buf,(rule->fw_op == FW_OP_ALLOW)?"pass ":"block ", len);strlcat(buf,(rule->fw_dir == FW_DIR_IN)?"in ":"out ", len);snprintf(tmp,sizeof(tmp),"on %s ", rule->fw_device);strlcat(buf, tmp, len);if(rule->fw_proto !=0){snprintf(tmp,sizeof(tmp),"proto %d ", rule->fw_proto);strlcat(buf, tmp, len);}// 源地址if(rule->fw_src.addr_type != ADDR_TYPE_NONE){snprintf(tmp,sizeof(tmp),"from %s ",addr_ntoa(&rule->fw_src));strlcat(buf, tmp, len);}elsestrlcat(buf,"from any ", len);// 源端口(TCP/UDP)if(rule->fw_proto == IP_PROTO_TCP || rule->fw_proto == IP_PROTO_UDP){if(rule->fw_sport[0]== rule->fw_sport[1])snprintf(tmp,sizeof(tmp),"port = %d ",rule->fw_sport[0]);elsesnprintf(tmp,sizeof(tmp),"port %d >< %d ",rule->fw_sport[0]-1, rule->fw_sport[1]+1);strlcat(buf, tmp, len);}// 目的地址if(rule->fw_dst.addr_type != ADDR_TYPE_NONE){snprintf(tmp,sizeof(tmp),"to %s ",addr_ntoa(&rule->fw_dst));strlcat(buf, tmp, len);}elsestrlcat(buf,"to any ", len);// 目的端口(TCP/UDP)或ICMP类型/代码if(rule->fw_proto == IP_PROTO_TCP || rule->fw_proto == IP_PROTO_UDP){if(rule->fw_dport[0]== rule->fw_dport[1])snprintf(tmp,sizeof(tmp),"port = %d",rule->fw_dport[0]);elsesnprintf(tmp,sizeof(tmp),"port %d >< %d",rule->fw_dport[0]-1, rule->fw_dport[1]+1);strlcat(buf, tmp, len);}elseif(rule->fw_proto == IP_PROTO_ICMP){if(rule->fw_sport[1]){snprintf(tmp,sizeof(tmp),"icmp-type %d",rule->fw_sport[0]);strlcat(buf, tmp, len);if(rule->fw_dport[1]){snprintf(tmp,sizeof(tmp)," code %d",rule->fw_dport[0]);strlcat(buf, tmp, len);}}}return(strlen(buf));}
以太网模块限制
7.1 Windows以太网存根实现
// eth-win32.c:20-51eth_t*eth_open(constchar*device){return(NULL);// Windows原生不支持}ssize_teth_send(eth_t*eth,constvoid*buf,size_t len){return(-1);}eth_t*eth_close(eth_t*eth){return(NULL);}inteth_get(eth_t*eth,eth_addr_t*ea){return(-1);}inteth_set(eth_t*eth,consteth_addr_t*ea){return(-1);}
原因:Windows内核不提供直接访问以太网层的API
7.2 变通方案
方案1:使用WinPcap/Npcap
// 需要安装WinPcap或Npcap#include<pcap.h>eth_t*eth_open_winpcap(constchar*device){char errbuf[PCAP_ERRBUF_SIZE];pcap_t*pcap = pcap_open_live(device,65535,1,1000, errbuf);return(eth_t*)pcap;}
方案2:原始套接字(有限功能)
// 只能发送IP层及以上的数据包SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);setsockopt(sock, IPPROTO_IP, IP_HDRINCL,&on,sizeof(on));
Windows平台限制详解
8.1 权限限制
8.1.1 原始套接字限制
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
代码示例:
// ip-win32.c:40-43if(setsockopt(ip->fd, IPPROTO_IP, IP_HDRINCL,(constchar*)&on,sizeof(on))== SOCKET_ERROR){SetLastError(ERROR_NETWORK_ACCESS_DENIED);// 权限不足return(ip_close(ip));}
8.1.2 防火墙限制
Windows防火墙默认阻止原始套接字操作,需要:
- 以管理员身份运行
- 配置防火墙规则
- 部分操作需要完全禁用防火墙(不推荐)
8.2 功能限制
8.2.1 缺失功能对比
|
|
|
|
|
|
|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8.2.2 IPv6支持差异
- Linux/BSD:原生支持IPv6的所有功能
- Windows:仅部分API支持IPv6(如
GetIpForwardTable2)
8.3 性能限制
8.3.1 API调用开销
Windows IP Helper API通过系统调用访问内核数据,比Linux的直接 /proc文件访问更慢。
性能对比:
Linux(读取/proc/net/route):~100μsWindows(GetIpForwardTable):~500μs -1ms
8.3.2 内存分配模式
// Windows:每次调用需要重新分配缓冲区for(len =sizeof(table[0]);;){free(table);table = malloc(len);ret =GetIpForwardTable(table,&len, FALSE);if(ret == NO_ERROR)break;}// Linux:可以直接读取固定大小的文件FILE*fp = fopen("/proc/net/route","r");while(fgets(line,sizeof(line), fp)){// 解析行}
8.4 安全限制
8.4.1 UAC (用户账户控制)
- 提示用户提升权限时可能被拒绝
- 某些环境下完全禁用UAC以使用libdnet
8.4.2 反病毒软件
原始套接字操作可能被标记为可疑行为:
- 关闭实时扫描
- 添加白名单
8.4.3 企业策略
企业环境可能:
- 禁用原始套接字
- 强制启用防火墙
- 禁止未签名驱动
各平台实现对比
9.1 网络接口管理对比
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9.2 IP模块对比
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9.3 路由模块对比
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9.4 ARP模块对比
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9.5 防火墙模块对比
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
变通方案与最佳实践
10.1 以太网功能实现
方案1:WinPcap/Npcap(推荐)
#include<pcap.h>typedefstruct{pcap_t*pcap;char device[256];}eth_winpcap_t;eth_winpcap_t*eth_open_winpcap(constchar*device){eth_winpcap_t*e = calloc(1,sizeof(*e));char errbuf[PCAP_ERRBUF_SIZE];e->pcap = pcap_open_live(device,65535,1,1000, errbuf);if(e->pcap == NULL){free(e);return NULL;}strncpy(e->device, device,sizeof(e->device)-1);return e;}int eth_send_winpcap(eth_winpcap_t*e,const u_char *buf,int len){return pcap_sendpacket(e->pcap, buf, len);}void eth_close_winpcap(eth_winpcap_t*e){if(e){pcap_close(e->pcap);free(e);}}
方案2:NDIS用户模式驱动
开发自定义NDIS驱动(复杂,不推荐用于普通应用)
10.2 权限提升方案
自动请求管理员权限
#include<shellapi.h>int restart_as_admin(){SHELLEXECUTEINFO sei ={0};sei.cbSize =sizeof(sei);sei.lpVerb ="runas";// 请求管理员权限sei.lpFile =GetCommandLine();sei.hwnd = NULL;sei.nShow = SW_NORMAL;if(!ShellExecuteEx(&sei)){DWORD dwError =GetLastError();if(dwError == ERROR_CANCELLED){printf("用户取消了权限提升请求\n");return-1;}}return0;}
运行时权限检查
int is_admin(){BOOL isAdmin = FALSE;PSID adminGroup = NULL;// 获取管理员组SIDSID_IDENTIFIER_AUTHORITY auth = SECURITY_NT_AUTHORITY;if(AllocateAndInitializeSid(&auth,2, SECURITY_BUILTIN_DOMAIN_RID,DOMAIN_ALIAS_RID_ADMINS,0,0,0,0,0,0,&adminGroup)){if(!CheckTokenMembership(NULL, adminGroup,&isAdmin)){isAdmin = FALSE;}FreeSid(adminGroup);}return isAdmin;}
10.3 性能优化建议
10.3.1 缓存接口信息
typedefstruct{IP_ADAPTER_ADDRESSES *iftable;time_t last_update;time_t cache_timeout;}intf_cache_t;intf_cache_t*intf_cache_create(int timeout){intf_cache_t*cache = calloc(1,sizeof(*cache));cache->cache_timeout = timeout;return cache;}int intf_cache_refresh(intf_cache_t*cache){time_t now = time(NULL);// 检查是否需要刷新if(cache->iftable &&(now - cache->last_update)< cache->cache_timeout){return0;// 使用缓存}// 刷新缓存if(cache->iftable) free(cache->iftable);ULONG len =16384;IP_ADAPTER_ADDRESSES *p;do{free(p);p = malloc(len);if(GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST,NULL, p,&len)!= ERROR_BUFFER_OVERFLOW){break;}}while(1);cache->iftable = p;cache->last_update = now;return0;}
10.3.2 批量操作优化
// 批量添加路由int route_batch_add(route_t*r,struct route_entry *entries,int count){int i, failed =0;for(i =0; i < count; i++){if(route_add(r,&entries[i])<0){failed++;}}return failed;}
10.4 错误处理增强
#include<stdio.h>void print_winsock_error(){int err =WSAGetLastError();char*msg = NULL;FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS,NULL,err,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPSTR)&msg,0,NULL);if(msg){fprintf(stderr,"Winsock错误 %d: %s\n", err, msg);LocalFree(msg);}else{fprintf(stderr,"Winsock错误 %d: 未知错误\n", err);}}void print_iphlpapi_error(DWORD err){char*msg = NULL;FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS,NULL,err,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPSTR)&msg,0,NULL);if(msg){fprintf(stderr,"IP Helper API错误 %d: %s\n", err, msg);LocalFree(msg);}else{fprintf(stderr,"IP Helper API错误 %d: 未知错误\n", err);}}
附录:完整代码示例
A.1 Windows平台网络工具完整示例
/** libdnet Windows网络工具完整示例* 功能:枚举接口、路由表、ARP表*/#include<stdio.h>#include<winsock2.h>#include"dnet.h"// 打印接口信息staticint print_interface(conststruct intf_entry *entry,void*arg){printf("\n[%s]\n", entry->intf_name);printf(" Flags: 0x%04X", entry->intf_flags);if(entry->intf_flags & INTF_FLAG_UP) printf(" UP");if(entry->intf_flags & INTF_FLAG_LOOPBACK) printf(" LOOPBACK");if(entry->intf_flags & INTF_FLAG_MULTICAST) printf(" MULTICAST");printf("\n");if(entry->intf_mtu !=0)printf(" MTU: %d\n", entry->intf_mtu);if(entry->intf_addr.addr_type == ADDR_TYPE_IP)printf(" IP: %s/%d\n", addr_ntoa(&entry->intf_addr), entry->intf_addr.addr_bits);if(entry->intf_link_addr.addr_type == ADDR_TYPE_ETH)printf(" MAC: %s\n", addr_ntoa(&entry->intf_link_addr));return0;}// 打印路由表staticint print_route(conststruct route_entry *entry,void*arg){printf("%-18s %-18s %-10s %5d\n",addr_ntoa(&entry->route_dst),addr_ntoa(&entry->route_gw),entry->intf_name,entry->metric);return0;}// 打印ARP表staticint print_arp(conststruct arp_entry *entry,void*arg){printf("%-18s %-18s\n",addr_ntoa(&entry->arp_pa),addr_ntoa(&entry->arp_ha));return0;}int main(void){WSADATA wsaData;intf_t*intf;route_t*route;arp_t*arp;// 初始化Winsockif(WSAStartup(MAKEWORD(2,2),&wsaData)!=0){fprintf(stderr,"WSAStartup失败\n");return1;}printf("=== libdnet Windows网络工具 ===\n");// 枚举接口printf("\n1. 网络接口:\n");if((intf = intf_open())!= NULL){intf_loop(intf, print_interface, NULL);intf_close(intf);}// 枚举路由表printf("\n2. 路由表:\n");printf("%-18s %-18s %-10s %5s\n","目标","网关","接口","度量");printf("------------------ ------------------ ---------- -----\n");if((route = route_open())!= NULL){route_loop(route, print_route, NULL);route_close(route);}// 枚举ARP表printf("\n3. ARP表:\n");printf("%-18s %-18s\n","IP地址","MAC地址");printf("------------------ ------------------\n");if((arp = arp_open())!= NULL){arp_loop(arp, print_arp, NULL);arp_close(arp);}WSACleanup();return0;}
A.2 跨平台抽象层
/** 平台特定头文件*/#ifdef _WIN32#include<winsock2.h>#include<windows.h>#include<iphlpapi.h>#pragma comment(lib,"ws2_32.lib")#pragma comment(lib,"iphlpapi.lib")#else#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<unistd.h>#endif/** 平台特定宏*/#ifdef _WIN32#define SOCKET_INVALID INVALID_SOCKET#define socket_close closesocket#define socket_errno WSAGetLastError()#define sleep_ms(ms)Sleep(ms)#else#define SOCKET_INVALID -1#define socket_close close#define socket_errno errno#define sleep_ms(ms) usleep((ms)*1000)#endif/** 初始化函数*/#ifdef _WIN32int network_init(void){WSADATA wsaData;returnWSAStartup(MAKEWORD(2,2),&wsaData);}void network_cleanup(void){WSACleanup();}#elseint network_init(void){return0;// Linux/macOS无需初始化}void network_cleanup(void){// 无需清理}#endif
总结
Windows平台关键限制
- 以太网层访问:完全不支持,必须依赖WinPcap/Npcap
- 原始套接字接收:Windows 8+严格限制
- 权限要求:大部分功能需要管理员权限
- 第三方防火墙:需要安装额外驱动
- API性能:比Linux直接访问
/proc慢
设计哲学
libdnet在Windows上采用零依赖的设计理念:
- 完全使用Windows原生API
- 通过IP Helper API实现大部分功能
- 对于不可实现的功能(如以太网),提供存根实现
- 通过动态加载实现版本兼容
适用场景
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
最佳实践
- 权限检查:运行时检查并提示用户提升权限
- 错误处理:使用
GetLastError()和FormatMessage()提供详细错误信息 - 版本兼容:使用动态加载支持旧版本Windows
- 缓存机制:缓存接口信息减少API调用开销
- Unicode支持:正确处理宽字符与多字节字符转换
-
公众号:安全狗的自我修养
-
vx:2207344074
-
http://gitee.com/haidragon
-
http://github.com/haidragon
-
bilibili:haidragonx
-


夜雨聆风