Μετατροπή ενός απλού tcp client σε port scanner
Λίγα λόγια για το tcpscan
Μπορείτε να το βρείτε εδώ.
Με το πρόγραμμα tcpscan
μπορείτε να πραγματοποιήσετε tcp συνδέσεις με απομακρυσμένους
υπολογιστές σε μία πόρτα που θα ορίσετε από τη γραμμή εντολών, καθώς
τρέχετε το πρόγραμμα, πχ: tcpscan Scylla.cs.uoi.gr 22
Το tcpscan
για να συνδεθεί με τον απομακρυσμένο υπολογιστή χρησιμοποιεί υποδοχές
(sockets), και την κλήση connect. Θα εξηγήσουμε τα βασικά του σημεία:
To
αρχείο etc/protocol του linux περιέχει πληροφορίες για τα διαθέσιμα
πρωτόκολλα δικτύου. Εμείς το μόνο που έχουμε να κάνουμε είναι να
ψάξουμε σε αυτό το αρχείο το αναγνωριστικό του πρωτοκόλλου tcp που θα
χρησιμοποιήσουμε. Για να το πετύχουμε αυτό χρησιμοποιούμε τη συνάρτηση getprotobyname:
struct protoent *protocol;
protocol = getprotobyname( "tcp" );
Στη συνέχεια χτίζουμε τις υποδοχές με της οποίες θα γίνει η επικοινωνία με τη συνάρτηση socket:
int sd;int rval;
int port;
sd = socket( PF_INET, SOCK_STREAM, protocol->p_proto );
Το sd είναι πλέον το αναγνωριστικό της υποδοχής μας.
Απομένει
να συνδεθούμε με τον απομακρυσμένο υπολογιστή. Το linux και όλη
οικογένεια λειτουργικών POSIX μας προσφέρει τη συνάρτηση:
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
- sockfd: το αναγνωριστικό της υποδοχής που θα χρησιμοποιήσουμε
- struct sockaddr:
Η διεύθυνση του απομακρυσμένου υπολογιστή. Εμείς θα χρησιμοποιήσουμε
την sockaddr_in struct διότι μας προσφέρει τη δυνατότητα εκτός από τη
διεύθυνση να ορίσουμε και πόρτα επικοινωνίας. - addrlen: Το μήκος της διεύθυνσης.
memset( &socketaddr, 0, sizeof(socketaddr)); /* initialize it */
socketaddr.sin_family = AF_INET; /* set the fam type to Internet */
socketaddr.sin_port = htons( port ); /* set port to argv[2] */
Τώρα απομένει να χτίσουμε τη διεύθυνση του απομακρυσμένου υπολογιστή. Σε αυτό θα μας βοηθήσει η συνάρτηση struct hostent *gethostbyname(const char *name). Το όρισμά της μπορεί να είναι είτε μια διεύθυνση IP είτε ένα όνομα (hostname):
hostaddr = gethostbyname(argv[1]);if ( !hostaddr )
{
fprintf( stderr, "gethostbyname(): %s\n", hstrerror(h_errno) );
return (h_errno);
}
/* Now we'll use memcpy() to place it in our sockaddr_in struct: */
memcpy( &socketaddr.sin_addr, hostaddr->h_addr, hostaddr->h_length );
/* At this point, we're ready to connect to the remote host: */
rval = connect( sd, (struct sockaddr *) &socketaddr, sizeof(socketaddr) );
if ( rval == -1 )
{
perror( "connect()" );
return (errno);
}
Πλέον
έχουμε συνδεθεί επιτυχώς με το απομακρυσμένο σύστημα και μπορούμε να
ανταλλάξουμε πληροφορίες. Αυτό το κομμάτι όμως δε θα μας απασχολήσει
εδώ.
Μετατροπή σε port scanner
Είναι πολύ εύκολο να μετατρέψουμε το παραπάνω πρόγραμμα σε ένα port scanner. Aς δούμε πως:
Αφαιρούμε
από το tcpconnect τις γραμμές κώδικα που υλοποιεί τη μεταβίβαση
μηνυμάτων δηλαδή από τη γραμμή printf("\nConnection with %s on
port %d established!\n\a",argv[1],port); και κάτω.
Προσθέτουμε τις παρακάτω γραμμές σε ένα loop:
for (port=0;port<PORT_MAX;port++) {
if (setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv))
{
perror("setsockopt");
return -1;
}
memset( &socketaddr, 0, sizeof(socketaddr)); /* initialize it */
socketaddr.sin_family = AF_INET; /* set the fam type to Internet */
socketaddr.sin_port = htons( port ); /* set port to argv[2] */
hostaddr = gethostbyname(argv[1]);
if ( !hostaddr )
{
fprintf( stderr, "gethostbyname(): %s\n", hstrerror(h_errno) );
return (h_errno);
}
memcpy( &socketaddr.sin_addr, hostaddr->h_addr, hostaddr->h_length );
[code]rval = connect( sd, (struct sockaddr *) &socketaddr, sizeof(socketaddr) );
if ( rval == -1 )
{
perror( "connect()" );
return (errno);
}
close(sd);
}
Και κάτω από το if (rval == -1) προσθέτουμε τον εξής κώδικα:
else {
printf(“Found OPEN port: %d\n”,port);
}
Πλέον,
ο port scanner μας είναι έτοιμος, αλλά έχει ένα μικρό προβληματάκι.
Πολλές φορές η connect δε μπορεί να συνδεθεί κατευθείαν σε μία πόρτα,
οπότε περιμένει ένα ακαθόριστο χρονικό διάστημα μέχρι να συνδεθεί η να
απορριφθεί η αίτηση σύνδεσης. Το φαινόμενο αυτό κάνει τον port scanner
μας αργό.
Για να αντιμετωπίσουμε το παραπάνω πρόβλημα χρησιμοποιούμε non_blocking socket, τη συνάρτηση select και δομή timeval:
#include <fcntl.h>
fd_set myset;
struct timeval tv;
socklen_t lon;
int valopt;
int lp,hp,ans;
Με την παρακάτω εντολή μετατρέπουμε την υποδοχή μας σε non-blocking:
fcntl(sd, F_SETFL, (flags = fcntl(sd,F_GETFL)) | O_NONBLOCK );
Αυτό
σημαίνει ότι κάθε κλήση connect θα επιστρέφει -1 δηλαδή κωδικό λάθους.
Το κλειδί εδώ είναι ότι ο κωδικός λάθους που θα επιστρέφεται σε
περίπτωση timeout θα είναι γνωστός: EINPROGRESS
Οπότε
το μόνο που πρέπει να κάνουμε είναι να ελέγξουμε αρχικά με μία if τον
κωδικό λάθους. Αν αυτός είναι ο EINPROGRESS, τότε καθορίζουμε το χρόνο
του timeout :
tv.tv_sec = TIMEOUT_TIME;
tv.tv_usec = 0;
Στη συνέχεια αρχικοποιούμε τον περιγραφέα myset που θα χρειαστούμε για τη select με τον περιγραφέα της υποδοχής μας:
FD_ZERO(&myset);
FD_SET(sd, &myset);
Έπειτα καλούμε τη select:
if (select(sd+1, NULL, &myset, NULL, &tv) > 0) {
lon = sizeof(int);
getsockopt(sd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon);
if (!valopt) {
printf("--- Found OPEN Port: %d\n",port);
} } else { printf("------ TIMEOUT on port: %d\n",port);
}
Η
select μας βοηθάει να προσδιορίσουμε μια συνθήκη λάθους για κάποιον από
τους περιγραφείς μας. Η σύνταξή της είναι η ακόλουθη:
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
FD_SET(int fd, fd_set *fdset); FD_CLR(int fd, fd_set *fdset);
FD_ISSET(int fd, fd_set *fdset);
FD_ZERO(fd_set *fdset);
Για περισσότερες πληροφορίες σχετικά με τη select ανατρέξτε εδώ
Κατεβάστε το ολοκληρωμένο TCPscan από εδώ
Active forum topics
- Wake up from suspend mode failed
- Οργάνωση στις μεταφράσεις των ελληνικών στο Debian.
- απορια σχετικα με nvidia440
- πρόβλημα εγκατάστασης nvidia driver GTS250.
- kernel 2.6.38 για squeeze
- Gnome3
- 2 μήνες σπασμένο το upgrade σε unstable [SOLVED]
- Οι χρήστες του Internet Explorer είναι χαζοί;
- Debian Lenny + Nvidia drivers
- εγκατασταση usb cosmote
New forum topics
Νέοι χρήστες
- kountp
- Adonthegreat
- giorgos_m
- prodromos
- airmaik
Οι πιο πρόσφατες καταχωρίσεις blog.
- Fosscomm 2012
- armhf officially into Debian!
- back from UDS/Linaro Connect...
- FTF is over, Linaro Interview
- straight from FTF 2011, San Antonio...
- Όσο μεγαλώνεις μαθαίνεις...
- Emdebian 2011 Sprint report, Genesi Europe store goes online...
- Θεσσαλονίκη - open source ERP workshop
- Χρήσιμο URL για Flash Cards και Linux filesystems
- Cambridge, UK, ARM HQ for Emdebian 2011 Sprint