Coding a SYN Scanner guide ( source included )

ithilgore advent.cloud.strife at gmail.com
Fri Mar 30 00:58:11 EEST 2007


Giorgos Keramidas wrote:
> On 2007-03-29 05:13, - <advent.cloud.strife at gmail.com> wrote:
>   
>> V13 wrote:
>>     
>>> Epeisis, to na xrisimopoieis etsi ta structs mallon problimata tha
>>> soy dimioyrgisei logo alignment kai reordering. Des to
>>> __attribute__((packed)) toy gcc. P.x. gia to IP:
>>>
>>> ----
>>> struct pseudo_hdr {
>>>         u_int32_t src;          /* 32bit source ip address*/
>>>         u_int32_t dst;          /* 32bit destination ip address */
>>>         u_char mbz;             /* 8 reserved bits (all 0)      */
>>>         u_char proto;           /* protocol field of ip header */
>>>         u_int16_t len;          /* tcp length (both header and data */
>>> } __attribute__((packed));
>>> ----
>>>
>>>   Des tin eksodo apo to parakato programma:
>>> ----
>>> #include <stdio.h>
>>>
>>> struct A { int a; char b; int c;};
>>>
>>> struct B { int a; char b; int c; } __attribute__((packed));
>>>
>>> int main()
>>> {
>>>         printf("%d\n%d\n", sizeof(struct A), sizeof(struct B));
>>> }
>>> ----
>>>
>>> v13 at hell:/tmp$ ./a
>>> 12
>>> 9
>>>
>>> Opos blepeis, logo alignment, to proto struct epiase 12 bytes giati
>>> to c egine align sta 32bit (4 byte), opote kai to c ksekinoyse apo to
>>> +2*4.  Ayto mporeis na to deis kanontas compile me to -Wpadded:
>>>       
>> thanx ! tetoiou eidous feedback perimenw.
>> implicit reordering mias struct den nomizw na ginetai kai sumfwna me
>> to C standard upo kanonikes sun8hkes den 8a prepei na ginetai
>>     
>
> Σωστός.
>
>   
>> Twra gia to packing ston sugekrimeno kwdika mono gia ka8ara
>> optimization skopus (  < size )  tha eixe isws nohma.
>>     
>
> Αυτό όμως είναι λίγο λάθος.  Έχει δίκιο ο Στέφανος (V13).  Αν κάνεις
> malloc() 12 bytes για ένα object τύπου "struct A", μπορεί ο memory
> allocator να σου επιστρέψει τα παρακάτω bytes:
>
>     A5 A5 A5 A5  A5 A5 A5 A5  A5 A5 A5 A5
>     ___________  __           ___________
>     a            b            c
>
> Αν εσύ διαβάσεις από 'raw data' ένα πακέτο που έχει τιμές:
>
>     a = 0x12345678 (4 bytes)
>     b = 0xCC       (1 byte)
>     c = 0x23456789 (4 bytes)
>
> χρησιμοποιώντας απλά read() για όλο το struct κι όχι read() για κάθε
> member ξεχωριστά, μπορεί να προσπαθήσεις να διαβάσεις περισσότερα bytes
> από ότι είναι διαθέσιμα στο packet stream.
>
> Αν από την άλλη, για κάποιο λόγο όντως προσπαθήσεις να διαβάσεις 9
> bytes, τα δεδομένα θα γραφτούν σε ένα struct A με τη μορφή:
>
>     12 34 56 78  CC 23 45 67  89 A5 A5 A5
>     ___________  __           ___________
>     a            b            c
>
> και δεν θα είναι πολύ σωστές οι τιμές των struct members.
>
>   
>> To pseudo header allwste xrhsimopoieitai mono sto checksuming kai me ena
>> sizeof upologizetai pada swsta to mege8os.
>>     
>
> Δηλαδή, ακριβώς λόγω των "padding bytes" μπορεί να κάνεις checksum σε
> λάθος δεδομένα (επειδή π.χ. δεν έκανες memset() σε μηδέν όλα τα bytes
> πριν από *κάθε* read() call).
>
> Δεν είναι πιο εύκολο να χρησιμοποιήσεις "packed structures"; :-
>
>   

Καλη παρατηρηση γενικα και απο τους 2. Καταλαβα τι εννοειτε αλλα στον 
συγκεκριμενο κωδικα συμβαινουν τα εξης:
α)  Το pseudo header χρησιμοποιειται **μονο** για το datagram που 
στελνουμε και **μονο** για τον υπολογισμο του
checksum : με αλλα λογια δεν χρησιμοποιειται πουθενα σε συνδυασμο με την 
read( )

Να θυμισω λιγο τον κωδικα :

        /* pseudo header for tcp checksum */
        struct pseudo_hdr *phdr = (struct pseudo_hdr *) ( datagram +
                       sizeof(struct sniff_ip) + sizeof(struct 
sniff_tcp) ) ;
        phdr->src = iph->ip_src.s_addr;
        phdr->dst = iph->ip_dst.s_addr;
        phdr->mbz = 0;
        phdr->proto = IPPROTO_TCP;
        phdr->len = ntohs (0x14);       /* in bytes the tcp segment 
length */
                                                /*- WhyTF is it network 
byte saved by default ????*/


        tcph->th_sum = htons ( checksum_comp ( (unsigned short *) tcph ,
                    sizeof(struct pseudo_hdr)+sizeof(struct sniff_tcp)));

        .....

        if (sendto (sockfd ,datagram, iph->ip_len , 0, (struct sockaddr 
*) &sin, sizeof (sin)) < 0) {
            fprintf ( stdout , "Error sending datagram for port %d\n",i);
            break;
        }


Επισης το ωραιο με αυτη τη struct ειναι οτι ειναι 12 (4+4+4(2+1+1))σε 
μεγεθος.  Παραθετω output του gdb :


80                      /* pseudo header for tcp checksum */
81                      struct pseudo_hdr *phdr = (struct pseudo_hdr *) 
( datagram +
82                                      sizeof(struct sniff_ip) + 
sizeof(struct sniff_tcp) ) ;
83                      phdr->src = iph->ip_src.s_addr;
84                      phdr->dst = iph->ip_dst.s_addr;
85                      phdr->mbz = 0;
86                      phdr->proto = IPPROTO_TCP;
87                      phdr->len = ntohs (0x14);       /* in bytes the 
tcp segment length */
(gdb) list
88                                                      /*- WhyTF is it 
network byte saved by default ????*/
89
90
91                      tcph->th_sum = htons ( checksum_comp ( (unsigned 
short *) tcph ,
92                                              sizeof(struct 
pseudo_hdr)+sizeof(struct sniff_tcp)));
93
94
95                      //printf ( "sizeof: %d \n" , sizeof(struct 
pseudo_hdr) ) ;
96                      //printf ( "sizeof: %d \n" , sizeof( *(phdr) ) ) ;
97
(gdb) b 89
Breakpoint 1 at 0x8049d1c: file syn_scanning.c, line 89.
(gdb) run -h 10.0.0.2 -S
Starting program: /home/ithilgore/linux_programming/port_scan/creeper -h 
10.0.0.2 -S
filter exp: src host 10.0.0.2
 Local IP: 10.0.0.4
Initiating Syn Scanning against 10.0.0.2 [1024 ports]

Breakpoint 1, Syn_Scanning () at syn_scanning.c:91
91                      tcph->th_sum = htons ( checksum_comp ( (unsigned 
short *) tcph ,
(gdb) print phdr
$1 = (struct pseudo_hdr *) 0xbfffe378
(gdb) x /20x 0xbfffe378
0xbfffe378:     0x0400000a      0x0200000a      0x14000600      0x00000000
0xbfffe388:     0x00000000      0x00000000      0x00000000      0x00000000
0xbfffe398:     0x00000000      0x00000000      0x00000000      0x00000000
0xbfffe3a8:     0x00000000      0x00000000      0x00000000      0x00000000
0xbfffe3b8:     0x00000000      0x00000000      0x00000000      0x00000000
(gdb)                                                                                          


Μας αφορουν οι  0xbfffe378:     0x0400000a      0x0200000a      0x14000600 
πχ εδω source ip = 10.0.0.4    dest ip = 10.0.0.2  ,  reserved bits = 
00  , ipproto =06 ,  header length = 1400
αλλα οτι και να ηταν απο τη στιγμη που ΔΕΝ κανουμε read δεν μας απασχολει.

β ) Επισης το επομενο σημειο ειναι τα headers που διαβαζουμε. Εκει δεν 
υπαρχει περιπτωση να διαβασουμε κατι παραπανω
καθως το ιδιο το *reading* το κανει η pcap με την dispatch οπου διαβαζει 
*πακετα* οχι απλα ενα καθορισμενο αριθμο bytes
Οποτε σε αυτη την περιπτωση ειναι λιγο δυσκολο να βγουμε εκτος οριων.

c) Τελος στο διαβασμα απο μας του πακετου ( στην got_packet ) ,  κανουμε 
access στο ιδιο το member της tcp struct  ( tcp->th_flags ) .

Θα πει κανεις ομως αν δεν ειναι packed τοτε πως ξερεις οτι εχει σωστη 
τιμη αυτο ?  Αυτο με παρακινησατε (και καλα κανατε) να το δω
παλι στον gdb )  οπου ειχα την εξης εξοδο:

Breakpoint 2, got_packet (args=0x5 <Address 0x5 out of bounds>, 
header=0xbfffe240,
    packet=0x806a7d2 "") at packet_analysis.c:37
37              if ( ( (tcp->th_flags & 0x02) == TH_SYN) && ( 
tcp->th_flags & 0x10 ) == TH_ACK ) {
(gdb) print ip
$2 = (const struct sniff_ip *) 0x806a7e0
(gdb) print tcp
$3 = (const struct sniff_tcp *) 0x806a7f4
(gdb) x /20x 0x806a7f4
0x806a7f4:      0xd2040100      0x00000000      0x6c8b4567      0x00001450
0x806a804:      0x000046a4      0x00000000      0x65640000      0x67726f03
0x806a814:      0x00010000      0x000cc001      0x00010001      0x00e7c400
0x806a824:      0x1b463e04      0x0010c076      0x00010002      0x00e7c400
0x806a834:      0x736e0306      0xc010c032      0x00020010      0xc4000001

κοιταξτε την πρωτη γραμμη:

0x806a7f4:      0xd2040100      0x00000000      0x6c8b4567      0x00001450

Αν συνεβαινε αυτο που λετε ,  τοτε το ACK sequence θα πρεπε να ηταν σε 
μια μη packed struct 0x00
και ομως ειναι 0x00000000
πραγμα απολυτως φυσιολογικο και καθωσπρεπει , αφου το ACK sequence ειναι 
32 bit .
Συνεπως ακομα και χωρις packed structs ο gcc και η libpcap που διαβασε 
στο data link layer μαλλον προνοησαν σωστα.
 Αλλα θα μπορουσε να σκεφτει κανεις και το αλλο:  Κοιταξτε τα 
<netinet/ip.h> και γενικα τα επισημα headers.
Δεν τα εχει πουθενα ως packed. Αυτο που λεω ειναι οτι θα μπορουσε να 
ειναι default συμπεριφορα ως attribute αλλα απο'τι
φαινεται δεν χρειαζεται στις περιπτωσεις αυτες.

Αυτα απο μενα. Καλη συζητηση παντως.

ithilgore








More information about the unix-admin-gr mailing list