> 文章列表 > Linux下C/C++ 网络扫描(主机扫描技术)

Linux下C/C++ 网络扫描(主机扫描技术)

Linux下C/C++ 网络扫描(主机扫描技术)

主机扫描是网络扫描的基础,通过对目标网络中主机IP地址的扫描,从一堆主机中扫描出存活的主机,然后以他们为目标进行后续的攻击。一般会借助于ICMP、TCP、UDP等协议的工作机制,检查打开的进程,开放的端口号等等。

主机扫描

主机扫描是在可达状态下检测,局域网下的 ARP 扫描和广域网下的 ICMP Echo 扫描、ICMP Sweep 扫描、ICMP Broadcast 扫描、ICMP Non-Echo 扫描都是基本的扫描技术。还有绕过防火墙和网络过滤设备的高级技术。

  • ARP 扫描

向目标主机所在的局域网发送 ARP 广播请求,在局域网连通状态下目标主机必定会响应正常的 ARP 广播请求。故而便可获得IP地址和 MAC 地址等信息。

...
int convertNetworkAddr(const char *inString,uint8_t outNetAddr[PROTOCOL_ADDRESS_LENGTH],uint8_t *netPrefix) 
{char *invalidCharPtr;// 读取3个八位字节for (int i = 0; i < PROTOCOL_ADDRESS_LENGTH - 1; ++i) {unsigned long octet = strtoul(inString, &invalidCharPtr, 10);if (octet >= 256 || *invalidCharPtr != '.') {return 1;}inString = invalidCharPtr + 1;outNetAddr[i] = (uint8_t) octet;}// 读取最后一个八位字节unsigned long octet = strtoul(inString, &invalidCharPtr, 10);if (octet >= 256 || *invalidCharPtr != '/') {return 1;}inString = invalidCharPtr + 1;outNetAddr[PROTOCOL_ADDRESS_LENGTH - 1] = octet;unsigned long prefix = strtoul(inString, &invalidCharPtr, 10);if (prefix > 32 || invalidCharPtr == inString) {return 1;}*netPrefix = prefix;// 按位AND ip地址和网络掩码uint8_t hostPartLength = PROTOCOL_ADDRESS_LENGTH * 8 - prefix;for (int j = 0; j < hostPartLength / 8; ++j) {outNetAddr[PROTOCOL_ADDRESS_LENGTH - 1 - j] = 0x00;}if (hostPartLength % 8) {uint8_t mask = (0xFF >> hostPartLength % 8) << hostPartLength % 8;outNetAddr[PROTOCOL_ADDRESS_LENGTH - hostPartLength / 8 - 1] &= mask;}return 0;
}
...
void printPacket(struct ArpPacket *p) 
{for (int i = 0; i < sizeof(struct ArpPacket); ++i) {int c = *((char*)p+i) & 0xFF;printf("%2x ", c);if (i%8 == 7)printf("\\n");}printf("\\n");
}int readNetworkAddr(const char *string,uint8_t dstAddr[PROTOCOL_ADDRESS_LENGTH],uint8_t *dstPrefix) 
{if (convertNetworkAddr(string, dstAddr, dstPrefix)) {fprintf(stderr, "Cannot recognize network address from: %s\\n", string);exit(1);}DEBUG(printf("========= RECOGNIZED NETWORK ADDRESS ===========\\n");printf("IP address: ");for (int j = 0; j < PROTOCOL_ADDRESS_LENGTH; ++j) {printf("%i ", (int)dstAddr[j]);}printf("\\nNet prefix: %i\\n", (int)(*dstPrefix) );)
}int main(int argc, char **argv) 
{
...if (argc != 3) {fprintf(stderr, "Usage: <interface name> <network address>.\\n""For example: scan eth0 192.168.0.1/24\\n");return 1;}// 打开套接字int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));if (s == -1) {perror("Error in opening socket: ");return 1;}// 读取网络地址uint8_t netAddr[PROTOCOL_ADDRESS_LENGTH];uint8_t netPrefix;readNetworkAddr(argv[2], netAddr, &netPrefix);DEBUG(setHostPart(netAddr, 255, 8);printf("IP address: ");for (int j = 0; j < PROTOCOL_ADDRESS_LENGTH; ++j) {printf("%i ", (int)netAddr[j]);}printf("\\n");)struct sockaddr_ll addr;prepareSockaddrll(&addr, argv[1]);struct ArpPacket packet;prepareArpPacket(&packet, &addr, argv[1]);DEBUG(printf("Packet hex dump: \\n");printPacket(&packet);)for (int i = 1; i < powl(2, (32 - netPrefix) ) - 1; ++i) {setHostPart(netAddr, i, 32 - netPrefix);setDstIP(netAddr, &packet);DEBUG(printf("Sending ARP request to IP ");printIP(netAddr, stdout);printf(" ..............");)if(sendto(s, &packet, sizeof(packet), 0,(struct sockaddr*) &addr, sizeof(addr)) == -1) {perror("Error in sending ARP request: ");fprintf(stderr, "Error in sending ARP request: %s\\n", strerror(errno));return 1;}DEBUG(printf("[ DONE ]\\n");)// waiting for responseusleep(200);struct ArpPacket response;ssize_t receivedResponseSize;if (receivedResponseSize = recv(s, &response, sizeof(response), MSG_DONTWAIT | MSG_TRUNC) > 0) {DEBUG(printf("Received %ld bytes of response: \\n", (long) receivedResponseSize);printPacket(&response);)if (memcmp(response.senderLogicAddress, packet.senderLogicAddress, sizeof(packet.senderLogicAddress))) {printf("IP ");printIP(response.senderLogicAddress, stdout);printf(" has MAC: ");for (int j = 0; j < HARDWARE_ADDRESS_LENGTH; ++j) {printf("%x:", (int)response.senderHardwareAddress[j]);}printf("\\n");}}}return 0;
}

运行结果:
Linux下C/C++ 网络扫描(主机扫描技术)

If you need the complete source code, please add the WeChat number (c17865354792)

tcpdump抓包
Linux下C/C++ 网络扫描(主机扫描技术)
Linux下C/C++ 网络扫描(主机扫描技术)

  • ICMP Echo 扫描

向目标主机发送ICMP Echo Request (type 8)数据包,等待回复的ICMP Echo Reply 包(type 0) 。如果能收到,则表明目标系统可达,否则表明目标系统已经不可达或发送的包被对方的设备过滤掉。

...
/*
获取网络接口的本地信息。*/
int loadLocalData( LocalData *dst, const char *ifname )
{struct ifreq nic;int sock = socket( AF_INET, SOCK_DGRAM, 0 );strncpy( nic.ifr_name, ifname, IFNAMSIZ-1 );nic.ifr_name[IFNAMSIZ-1] = '\\0';if( ioctl( sock, SIOCGIFINDEX, &nic ) < 0 ){close( sock );return -1;}dst->ifindex = nic.ifr_ifindex;// 分配的IP地址if( ioctl( sock, SIOCGIFADDR, &nic ) < 0 ){close( sock );return -1;}memcpy( &dst->ip, nic.ifr_addr.sa_data + 2, INET_ALEN );// MAC地址 if( ioctl( sock, SIOCGIFHWADDR, &nic ) < 0 ){close( sock );return -1;}memcpy( &dst->mac, nic.ifr_hwaddr.sa_data, ETH_ALEN );// 子网掩码if( ioctl( sock, SIOCGIFNETMASK, &nic ) < 0 ){close( sock );return -1;}memcpy( &dst->netmask, nic.ifr_netmask.sa_data + 2, INET_ALEN ); close( sock );return 0;
}/*** 为特定协议创建套接字。*/
int createSocket( SocketType type, int msecs )
{int sfd;struct timeval timer;if( type == ICMPSocket )sfd = socket( AF_INET, SOCK_RAW, IPPROTO_ICMP );elsesfd = socket( AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP) );if( sfd < 0 )return -1;// 设置接收数据的最大时间timer.tv_sec = msecs / 1000;timer.tv_usec = msecs % 1000 * 1000;if( setsockopt( sfd, SOL_SOCKET, SO_RCVTIMEO, &timer, sizeof(timer) ) < 0 ){close( sfd );return -1;}return sfd;
}
.../*** 计算增加一个IP地址的结果。*/
struct in_addr ipAddOne( struct in_addr ip )
{struct in_addr aux = { ntohl( ip.s_addr ) };aux.s_addr++;aux.s_addr = htonl( aux.s_addr );return aux;
}/*** 交换两个IP地址的值*/
void ipSwap( struct in_addr *a, struct in_addr *b )
{a->s_addr = a->s_addr ^ b->s_addr;    b->s_addr = a->s_addr ^ b->s_addr;a->s_addr = a->s_addr ^ b->s_addr;
}
...int main( int argc, char **argv )
{
...// 验证接口if( !interface ){fprintf( stderr, "Error: No interface given. Use -h for help\\n" );return -1;}// 加载本地数据if( loadLocalData( &data, interface ) < 0 ){perror( interface );return -1;}// 验证是否有特定的IP if( strFirst ){if( !inet_aton( strFirst, &first ) ){fprintf( stderr, "%s: Invalid address\\n", strFirst );return -1;}if( strLast ){ // 验证是否有范围if( !inet_aton( strLast, &last ) ){fprintf( stderr, "%s: Invalid address\\n", strLast );return -1;}else{total = ntohl( last.s_addr ) - ntohl( first.s_addr );if( total < 0 ){total = -total;ipSwap( &first, &last );}total++;}}else{total = 1;}}else{ // 如果没有,则从本地网络数据中获取范围。first.s_addr = data.ip.s_addr & data.netmask.s_addr;first = ipAddOne( first );last.s_addr = data.ip.s_addr | ~data.netmask.s_addr;total = ntohl( last.s_addr ) - ntohl( first.s_addr );}// 创建套接字if( (sfd = createSocket( type, waitTime ) ) < 0 ){perror( "Failed to create socket" );return 2;}...// 扫描周期for( int i = 1 ; i <= total && running ; i++, first = ipAddOne(first) ){printf( "\\r(%d%%) Testing %s...", (int)(100.0 / total * i), inet_ntoa(first) );fflush( stdout );if( first.s_addr == data.ip.s_addr ){printf( " (this host)\\n" );ups++;}else{switch( isUp(sfd, first, &data) ){case -1:perror( " send request" );break;case 1:puts( " is up" );ups++;}}}close( sfd );printf( "\\n%d hosts up\\n", ups );return 0;
}

运行结果:

Linux下C/C++ 网络扫描(主机扫描技术)

If you need the complete source code, please add the WeChat number (c17865354792)

tcpdump抓包:
Linux下C/C++ 网络扫描(主机扫描技术)
Linux下C/C++ 网络扫描(主机扫描技术)

  • ICMP Sweep 扫描

Linux下C/C++ 网络扫描(主机扫描技术)
Linux下C/C++ 网络扫描(主机扫描技术)

总结

这里我们就知道了其实主机扫描很简单,只需要证明其主机存活就好。我们只需要对目标主机发送特定的数据包,如果目标主机有回应,那么我们就认为该主机是存活的;反之如果对方不回应,我们就认为其不是存活主机。

Welcome to follow WeChat official account【程序猿编码