/***************************************************************************** SYNNER: TCP port scanner that sends a SYN and waits for the ACK-SYN to acknowledge that the port is live or ACK-RST that says it isn't. Linux only (too many issues with BSD, gave up). Compile: cc synner.c [-D OWN_IP_HDR] Use the OWN_IP_HDR for create our own IP header instead of leaving it to the kernel. Version: 20070318 *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #define OWN_IP_HDR */ #define VERSION 20070318 #define MAX_PORT 65535 #define NUM_SCANS 1 #define SCAN1_DELAY 1000 /*** I use my own header structures in case the structuress in /usr/include are updated at some point. Also I don't use bitfields so I don't have to worry about endian-ness at compile time. ***/ /* IP header. Stevens page 211 */ struct st_ip { u_char ip_vhl; /* Top 4 bits = version, bottom = header length */ u_char ip_tos; uint16_t ip_len; uint16_t ip_id; uint16_t ip_off; u_char ip_ttl; u_char ip_p; uint16_t ip_cksum; uint32_t ip_src; uint32_t ip_dst; }; /* TCP header. Stevens page 802 */ struct st_tcp { uint16_t th_sport; uint16_t th_dport; uint32_t th_seq; uint32_t th_ack; u_char th_off; /* Only top 4 bits used */ u_char th_flags; /* Only bottom 6 bits used */ uint16_t th_win; uint16_t th_cksum; uint16_t th_urp; } *tcp; /* Send and receive packet */ struct { struct st_ip ip; struct st_tcp tcp; } send_pkt; #define PACKET_LEN (sizeof(struct st_ip) + TCP_MAXWIN) union { u_char data[PACKET_LEN]; struct st_ip ip; } recv_pkt; /* Pseudo header & tcp for checksum calc */ struct { uint32_t src; uint32_t dest; u_char pad; u_char proto; uint16_t len; struct st_tcp tcp; } pseudo; /* List of ports that we've got info on */ struct { char replied; char live; } portinfo[MAX_PORT]; /* TCP flags */ enum { TCP_SYN = 2, TCP_RST = 4, TCP_ACK = 16 }; /* Command line vars */ int start_port; int end_port; int verbose; int num_scans; int check_acknum; int scan1_delay; /* Runtime vars */ struct sockaddr_in src_addr; struct sockaddr_in dest_addr; int sock; int replied_total; int port_count; int live_count; uint16_t source_port; uint32_t send_seqnum; /* Forward declarations */ void parseCmdLine(int argc, char **argv); void init(); void mainloop(); int getDelay(int scan_num); void sendPacket(uint16_t port); int recvPacket(int wait); uint16_t checksum(uint16_t *data, int len); /******************************** Start here ********************************/ int main(int argc, char **argv) { parseCmdLine(argc,argv); init(); mainloop(); return 0; } /*** Get the command line stuff ***/ void parseCmdLine(int argc, char **argv) { char *opt[] = { "d", "s", "sp", "ep", "scan", "delay", "chack", "vb", "v" }; enum { OPT_D, OPT_S, OPT_SP, OPT_EP, OPT_SCAN, OPT_DELAY, OPT_CHACK, OPT_VB, OPT_V, OPT_END }; struct hostent *hp; char *dest; char *src; int i,o; dest = NULL; src = NULL; start_port = 1; end_port = MAX_PORT; num_scans = NUM_SCANS; check_acknum = 0; verbose = 0; scan1_delay = SCAN1_DELAY; for(i=1;i < argc;++i) { if (argv[i][0] != '-') goto USAGE; for(o=0;o != OPT_END;++o) if (!strcasecmp(opt[o],argv[i]+1)) break; switch(o) { case OPT_CHACK: check_acknum = 1; continue; case OPT_VB: verbose = 1; continue; case OPT_V: printf("SYNNER version %u\n",VERSION); exit(0); } if (++i == argc) goto USAGE; switch(o) { case OPT_D: dest = argv[i]; break; case OPT_S: src = argv[i]; break; case OPT_SP: start_port = atoi(argv[i]); break; case OPT_EP: end_port = atoi(argv[i]); break; case OPT_SCAN: num_scans = atoi(argv[i]); break; case OPT_DELAY: scan1_delay = atoi(argv[i]); break; default: goto USAGE; } } if (!dest) goto USAGE; if (start_port < 1 || end_port < 1 || start_port > MAX_PORT || end_port > MAX_PORT) { puts("CMD ERROR: Port numbers must be between 1 and 65535."); exit(1); } if (end_port < start_port) { puts("CMD ERROR: The start port must be >= to the end port."); exit(1); } if (!num_scans) { puts("CMD ERROR: Number of scans must be > zero."); exit(1); } if (scan1_delay < 0) { puts("CMD ERROR: Delay must be >= zero."); exit(1); } /* Resolve addresses */ bzero(&dest_addr,sizeof(dest_addr)); dest_addr.sin_family = AF_INET; if ((dest_addr.sin_addr.s_addr = inet_addr(dest)) == -1) { if (!(hp = gethostbyname(dest))) { printf("ERROR: Destination address '%s' not found.\n",dest); exit(1); } memcpy((char *)&dest_addr.sin_addr.s_addr,hp->h_addr,4); } bzero(&src_addr,sizeof(src_addr)); src_addr.sin_family = AF_INET; if (src) { if ((src_addr.sin_addr.s_addr = inet_addr(src)) == -1) { if (!(hp = gethostbyname(src))) { printf("ERROR: Source address '%s' not found.\n",src); exit(1); } memcpy((char *)&src_addr.sin_addr.s_addr,hp->h_addr,4); } } else src_addr.sin_addr.s_addr = 0; return; USAGE: printf("Usage: %s\n" " -d \n" " [-s ]\n" " [-sp ]\n" " [-ep ]\n" " [-scan ] : Default = %d\n" " [-delay ] : Default = %d\n" " [-chack] : Check acknowledgement number and ignore\n" " the ipacket if wrong.\n" " [-vb] : Verbose\n" " [-v] : Print version then exit.\n", argv[0], NUM_SCANS, SCAN1_DELAY); exit(1); } #define NUM_INTERFACES 256 /*** Set up socket stuff ***/ void init() { struct ifconf ifc; struct ifreq *ifrp; struct sockaddr_in *ain; char *ptr; int intersock; int len; int i; uint32_t dest; #ifdef OWN_IP_HDR int on; #endif if (verbose) puts("\n*** SYNNER - TCP port scanner ***\n"); /* If no source address given then find one in the interfaces list. unless the destination is on the loopback subnet */ if (!src_addr.sin_addr.s_addr) { /* Going through interfaces - create any old datagram socket */ if ((intersock = socket(AF_INET,SOCK_DGRAM,0)) == -1) { perror("socket()"); exit(1); } /* Create data buffer */ len = sizeof(ifc) * NUM_INTERFACES; ifc.ifc_len = len; ifc.ifc_buf = (char *)malloc(len); /* Load interface data into buffer in ifc structure */ if (ioctl(intersock,SIOCGIFCONF,&ifc) == -1) { perror("ioctl()"); exit(1); } dest = ntohl(dest_addr.sin_addr.s_addr); /* Loop twice to get source address */ for(i=0;i < 2;++i) { /* Go through actual interface list. UNP page 476 */ for(ptr = (char *)ifc.ifc_req; ptr < (char *)ifc.ifc_req + ifc.ifc_len; ptr += len) { ifrp = (struct ifreq *)ptr; len = sizeof(ifrp->ifr_name); /* Ignore IP6 interfaces */ if (ifrp->ifr_addr.sa_family == AF_INET6) { len += sizeof(struct sockaddr_in6); continue; } /* Assume anything else is IP4 */ len += sizeof(struct sockaddr_in); ain = (struct sockaddr_in *)&ifrp->ifr_addr; /* On 2nd loop just get the first non loopback interface */ if (i) { if ((ntohl(ain->sin_addr.s_addr) & 0xFF000000) != 0x7F000000) goto SET; continue; } /* On 1st loop get an interface that has a matching 1st byte of IP with the dest IP */ if ((ntohl(ain->sin_addr.s_addr) & 0xFF000000) == (dest & 0xFF000000)) goto SET; } } puts("ERROR: Unable to find suitable source IP address. Please set manually."); exit(1); SET: if (verbose) { printf("Setting source address to %s on interface '%s'...\n", inet_ntoa(ain->sin_addr), ifrp->ifr_name); } src_addr.sin_addr.s_addr = ain->sin_addr.s_addr; close(intersock); free(ifc.ifc_buf); } /* UNP book says we shouldn't be able to receive TCP packets on a raw socket , but it seems to work. */ if ((sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP)) == -1) { perror("socket()"); exit(1); } #ifdef OWN_IP_HDR /* Want to create our own IP header */ on = 1; if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on)) == -1) { perror("setsockopt()"); exit(1); } #endif port_count = end_port - start_port + 1; send_seqnum = random() % (uint32_t)-1; source_port = random() % (MAX_PORT + 1); bzero(&portinfo,sizeof(portinfo)); replied_total = 0; live_count = 0; } /*** Send and receive packets ***/ void mainloop() { int port; int i; int delay; /* Make 3 attempts going slower each time */ for(i=0;i < num_scans;++i) { delay = getDelay(i); if (verbose) printf("\nSCAN: %d (%d usec delay)\n",i+1,delay); for(port=start_port;port <= end_port;++port) { if (!portinfo[port].replied) { sendPacket((uint16_t)port); if (delay) usleep(delay); } /* Read as many packets as possible */ while(recvPacket(0)); } /* Can't just check replied_count since we could get duplicates */ for(port=start_port;port <= end_port;++port) if (!portinfo[port].replied) break; if (verbose) printf("\n Received %d replies in total so far...\n", replied_total); if (port > end_port) goto DONE; } if (verbose) puts("\nWaiting for stragglers..."); for(i=0;i < 10;++i) { while(recvPacket(250000)); } DONE: if (verbose) { if (live_count) { puts("\nLIVE PORTS:"); for(port=start_port;port <= end_port;++port) { if (portinfo[port].live) printf(" %d\n",port); } } printf("\nTotal of %d ports live (%d replies from %d ports)\n\n", live_count,replied_total,port_count); } } /*** Get the delay in microseconds for the given scan number ***/ int getDelay(int scan_num) { switch(scan_num) { case 0: return scan1_delay; case 1: return 10000; case 2: return 50000; } return 100000; } /*** Send a packet ***/ void sendPacket(uint16_t port) { int i; if (verbose) printf("\r Scanning port: %05d",port); fflush(stdout); /* Set up pseudo header & tcp header for checksum calc */ bzero(&pseudo,sizeof(pseudo)); pseudo.src = src_addr.sin_addr.s_addr; pseudo.dest = dest_addr.sin_addr.s_addr; pseudo.proto = IPPROTO_TCP; pseudo.len = htons(sizeof(pseudo.tcp)); pseudo.tcp.th_sport = htons(source_port); pseudo.tcp.th_dport = htons(port); pseudo.tcp.th_seq = htonl(send_seqnum); pseudo.tcp.th_ack = 0; pseudo.tcp.th_off = 5 << 4; pseudo.tcp.th_flags = TCP_SYN; pseudo.tcp.th_win = htons(1000); /* For some reason which I don't understand if you use htons() on the checksum it makes it invalid. But....? */ pseudo.tcp.th_cksum = checksum((uint16_t *)&pseudo,sizeof(pseudo)); /* Set up IP packet */ bzero(&send_pkt,sizeof(send_pkt)); #ifdef OWN_IP_HDR /* Might want to create our own IP header to randomise the ip ids */ send_pkt.ip.ip_vhl = 4 << 4; send_pkt.ip.ip_vhl |= 5; /* 5 x 4-byte-words */ send_pkt.ip.ip_tos = IPTOS_LOWDELAY; send_pkt.ip.ip_len = htons(sizeof(send_pkt)); send_pkt.ip.ip_id = htons(random() % 65536); send_pkt.ip.ip_off = 0; send_pkt.ip.ip_ttl = 64; send_pkt.ip.ip_p = IPPROTO_TCP; send_pkt.ip.ip_src = src_addr.sin_addr.s_addr; send_pkt.ip.ip_dst = dest_addr.sin_addr.s_addr; send_pkt.ip.ip_cksum = 0; /* Kernel will calc IP header checksum */ #endif memcpy(&send_pkt.tcp,&pseudo.tcp,sizeof(send_pkt.tcp)); /* Try sendto() 3 times before giving up if we get an interrupt or a block */ for(i=0;i < 3;++i) { /* For some reason sendto() wants a destination address even though its set in our IP header */ if ((sendto( #ifdef OWN_IP_HDR sock,&send_pkt,sizeof(send_pkt),0, #else sock,&send_pkt.tcp,sizeof(send_pkt.tcp),0, #endif (struct sockaddr *)&dest_addr,sizeof(dest_addr))) == -1) { perror("sendto()"); if (errno == EINTR || errno == EWOULDBLOCK) { usleep(100000); continue; } if (i == 2) printf("WARNING: Failed to send to port %d\n",port); continue; } break; } } /*** Read a packet ***/ int recvPacket(int wait) { struct sockaddr addr; struct timeval tm; fd_set mask; uint addrlen; int rlen; int hlen; tm.tv_sec = 0; tm.tv_usec = wait; FD_ZERO(&mask); FD_SET(sock,&mask); switch (select(FD_SETSIZE,&mask,0,0,&tm)) { case -1: perror("select()"); exit(1); case 0: return 0; } if (!FD_ISSET(sock,&mask)) { puts("ERROR: socket not set"); exit(1); } addrlen = sizeof(addr); if ((rlen = recvfrom( sock,recv_pkt.data,PACKET_LEN,0,&addr,&addrlen)) == -1) { perror("recvfrom()"); exit(1); } /* IP header is 20 bytes if no options. If there are options TCP header will be in a different place hence can't just put the st_tcp struct in recv_pkt union */ hlen = (int)(recv_pkt.ip.ip_vhl & 0x0F) << 2; if (rlen < hlen) return 1; /* Bad packet , ignore it */ tcp = (struct st_tcp *)(recv_pkt.data + hlen); /* Return 1 since we still read a packet which means theres still data around */ if (recv_pkt.ip.ip_p != IPPROTO_TCP || recv_pkt.ip.ip_src != dest_addr.sin_addr.s_addr || ntohs(tcp->th_dport) != source_port || (check_acknum && ntohl(tcp->th_ack) != send_seqnum + 1)) return 1; tcp->th_sport = ntohs(tcp->th_sport); /* Only interested in an ACK-SYN or ACK-RST */ if ((tcp->th_flags & TCP_ACK) && !portinfo[tcp->th_sport].replied) { if (tcp->th_flags & TCP_SYN) { portinfo[tcp->th_sport].replied = 1; replied_total++; portinfo[tcp->th_sport].live = 1; live_count++; /* If not in verbose mode print port immediately */ if (!verbose) printf("%d\n",tcp->th_sport); } else if (tcp->th_flags & TCP_RST) { portinfo[tcp->th_sport].replied = 1; replied_total++; } } return 1; } /*** Standard IP checksum algorithm. Stevens page 236 ***/ uint16_t checksum(uint16_t *data, int len) { uint32_t sum = 0; while (len > 1) { sum += *data++; if (sum & 0x80000000) sum = (sum & 0xFFFF) + (sum >> 16); len -= 2; } if (len) sum += (uint16_t) *(u_char *)data; while(sum >> 16) sum = (sum & 0xffff) + (sum >> 16); return ~sum; }