/* Los uloha 1 verze 3 */ #include #include #include #include #include using namespace std; /* Uzivaji se datove typy definovane PCAPem, u kterych by mela byt zarucena spravna velikost. */ //pro jedna bude debugovat do souboru debug.txt vsechan TCP data #define F_DEBUG 1 //debug HTTP dat na obrazovku //#define S_DEBUG 1 #define ETHER_ADDR_LEN 6 //////////////////////////////////////////////////////////////////////////////// //masky pro flagy ETH paketu #define TYPE_IP 0x0008 #define TYPE_ARP 0x0608 #define TYPE_RARP 0x3508 #define PROTOCOL_TCP 6 //promenna, ktera se budep ouzivat pro seq a ack cisla v TCP paketech #define th_seq u_int //////////////////////////////////////////////////////////////////////////////// //tady uzijeme jednu uplne naprosto uzasnou Cckovou vec // //uvazime, ze PCAP ma nekde sve pochytane ramce jako retez bitu (bytu) // //tak si nadefinujeme hezkou strukturu s promennymi, tak aby odpovidalo //poradi jednotlivych kousku jednicek a nul jak jsou definovane v RFC //(muzeme osetrit i skutecnou velikost promenne v bitech) //a placneme ji na pozdavany kousek jednicek a nul //a heureka - krasne vidime data tak, jak je potrebujeme //muzeme k pozadovanym 1 a 0 pristupovat hezky pomoci jmen //(jen je treba osetrit BigEndian a LittleEndian pozice) //Dle definice jsou data v IP ulozena jako BE. // // At si kdo chce rika co chce, // C rulez!!!!! ;-) // //////////////////////////////////////////////////////////////////////////////// // //tak tady je treba fikane vyresit jak placnout strukturu na spravne misto // a je adresa pocatku ramce //eth masku placneme na jeho zacatek #define ETH(a) ((struct mask_ethernet*)(a)) //ip masku za delku eth. masky #define IP(a) ((struct mask_ip*)(a + sizeof(mask_ethernet))) //tcp masku placneme za ip i ath masku #define TCP(a) ((struct mask_tcp*)(a + sizeof(mask_ethernet) + sizeof(mask_ip))) //a jeste vtyhytavka, define pro pociteni velikosti datove casti TCP paketu //funkce pro prevraceni hodnot jsou definovane az pozdeji, to DEFINU nevadi #define TCPDATALEN ( ushortReverse(IP(aktualni_ramec)->ip_len) - (IP(aktualni_ramec)->ip_hl*4 + TCP(aktualni_ramec)->tcp_off*4 )) // -24 //////////////////////////////////////////////////////////////////////////////// // precteny eth ramec bude viden pres tuto strukturu//////////////////////////// struct mask_ethernet { //Destination host address u_char ether_dhost[ETHER_ADDR_LEN]; //Source host address u_char ether_shost[ETHER_ADDR_LEN]; //IP? ARP? RARP? etc u_short ether_type; }; //////////////////////////////////////////////////////////////////////////////// // precteny ip paket bude viden pres strukturu, ktera odpovida jeho deifinici/// struct mask_ip { /* version verze ip protokolu */ u_char ip_v:4; /* header length delka headru IP paketu - v 32bitovych slovech -> zaroven take ukazatel na prvni data */ u_char ip_hl:4; /* type of service Polozka v praxi nepouzivana */ u_char ip_tos; /* total length Celkova delka datagramu v bytech. */ u_short ip_len; /* identification Identifikacni cislo k rozpoznani fragmentu stejneho paketu */ u_short ip_id; /* fragment offset field Pozice zacatku dat tohoto fragmentu v celkovem paketu */ u_short ip_off; /* maksy flagu: -- flagy budeme kotrolovat z offsetu, se nepodarilo hezky rozseknout */ /* reserved fragment flag nepouzivat! */ #define IP_RF 0x8000 #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ /* pres tuhle masku zjistime skutecny offset*/ #define IP_OFFMASK 0x1fff /* time to live Maximalni pocet "hopu", ktere jeste datagram prezije */ u_char ip_ttl; /* protocol Cislo (typ) protokolu uzite v datove casti */ u_char ip_p; /* checksum kontrolni soucet */ u_short ip_sum; /* source and dest address Adresa cile a zdroje ve strukture 4 x 1 byte */ struct in_addr ip_src,ip_dst; //options nás nezajímají }; //////////////////////////////////////////////////////////////////////////////// // Podobne, struktura pro TCP paket //////////////////////////////////////////// struct mask_tcp { /* source port zdrojovy port */ u_short tcp_sport; /* destination port cilovy port */ u_short tcp_dport; /* sequence number cislo prvniho vysilaneho bytu v tomto segmentu */ th_seq tcp_seq; /* acknowledgement number cislo prvniho ocekavaneho bytu */ th_seq tcp_ack; /* data offset velikost TCP headeru */ u_char tcp_off:4; /* (unused) Rezervovano, musi by 0 -- rfc rika 6 bitu -- ale tim, ze ty 2 dalsi jsou 0 -- tak je vrznem do dalsi promenne -- prekazet tam nebudou, a snaze se to rozparceluje */ u_char tcp_x2:4; /* flagy */ u_char tcp_flags; /* masky jednotlivych flagu */ #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 #define TH_ECE 0x40 #define TH_CWR 0x80 //maska pro vsechny falgy #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR) /* window velikost okenka */ u_short tcp_win; /* checksum kontrolni soucet */ u_short tcp_sum; /* urgent pointer ---uplatni se jenom v segmentech s URG flagem */ u_short tcp_urp; /* options nas nezajimaji --jejich pozice je osetrena pri placani masky */ }; //pomocna definice class datasegment; //takhle ulozime datovou cast TCP paketu //aby se s tim pak dalo hezky pracovat // - nahazet vsechny kousky do listu a seradit. ;-) class datasegment { public: //cislo - sequence number th_seq seq; //data char *data; int velikost; //a porovnaci operator, abychom mohli //datasegmenty hezky seradit pomoci vestavenych funkci. bool operator< (const datasegment *ds2); }; bool datasegment::operator< (const datasegment *ds2) { return (this->seq < ds2->seq); } //uh, nejaka cecka se vztekala a chtela < takto - dva argumenty //typu trida (reference na tridu) bool operator< (const datasegment &ads1, const datasegment &ads2) { return ((ads1.seq)<(ads2.seq)); } //////////////////////////////////////////////////////////////////////////////// //tato struktura bude zastresovat soubor s odchytanymi daty///////////////////// class Pochytane_Ramce { //debug veci #ifdef F_DEBUG FILE *fd; #endif //interni ukzatel PCAPU na otevreny soubor s daty pcap *fhandle; //jmeno toho souboru //(potrbujeme, muzeme jej znovu otevirat) char *fname; //udava cislo TCP spojeni, ktere nas zajima blize (zadava se na vstupu) //cisluji se pokusy o navazani spojeni od 0 int filter; //pro 1 zapise zachyvene spojeni do souboru //pro 2 bude vypisovat na obrazovku int output_type; //... struct pcap_pkthdr header; //pamatuje si ukazatel na prave zpracovavany eth ramec //(realizaci si resi PCAP vnitrne) // pokud se rovna 0 - tak je prazdny const u_char *aktualni_ramec; char HTTPBuffer[1<<20]; //ukazatel na pochytana data patrici HTTP char *HTTPBufferPtr; //do techto lsitu nalejeme data segmenty z TCP //list je umozni hezky elegantne urovnat a dale s nimi pracovat list stream_in, stream_out; public: //konstruktor - dostane jmeno souboru s TCP DUMP zaznamem Pochytane_Ramce(char *input_fname); //destruktor ~Pochytane_Ramce(); //////////////////////////////////////// //funkce jsou popsane u jejich definic// //nastavuji hodnotyp romennych teto tridy void setFilter(int); void setOutputType(const int &); //pomocne funkce, ktere resi to //ze si Intel zvolil pro 08086 LE zatimco NWG pro protokol BE //Tady trpime kvuli necemu, co se rozhodli pred skoro dvaceti lety //zadratovat. //A to jsou mezi nami lide, kteri si mysli, ze cim vice //se toho zadratuje na pevno do procesoru //(napriklad spravu datovych typu v inteligentni pameti) //, tim bude lepe u_int uintReverse(const u_int &) const; u_short ushortReverse(const u_short &) const; void restart(); unsigned int naZacatekSpojeni(int); //pohyb v paketech //lepe skakani dopredu podle typu bool nextPacket(); bool nextIPPacket(); bool nextTCPPacket(); bool nextConnectionPacket(); //pomocne vypisovaci //parametry se vetsionou predavaji jako konstantni reference //tj. uplne bez kopirovani //metody jsou oznaceny const //thole pobezi s vetrem o zavod! void vypis_ip_adresu(const struct in_addr &ip) const; void vypis_sipecku(const int &) const; void vypis_tcp_flagy() const; void vypis_seq_a_ack(const u_int &, const u_int &) const; void prehledTCP(); void priviraniOkna(u_short &, const u_short &); int navazanychSpojeni(); bool opakovanyPaket(const u_int &, const u_int &) const; ////////////////////////////// //finalni kompletni dulezite// void prozkoumej_zacycena_data(); void analyzujHTTPPacket(void); void analyzujSpojeni(); }; //konstruktor Pochytane_Ramce::Pochytane_Ramce(char *input_fname) { #ifdef F_DEBUG fd = fopen("f_debug.txt", "wb"); #endif //interni struktura PCAPU pro pristup k souboru fhandle=NULL; //jmeno souboru fname=input_fname; //standardne nastavime filtr na 0 - nefiltruj filter=0; //standardne nezapisujeme vystup do souboru output_type=0; //otevre soubor s daty - nastavi ukazatel na zacatek restart(); } //destruktor Pochytane_Ramce::~Pochytane_Ramce() { #ifdef F_DEBUG fclose(fd); #endif pcap_close(fhandle); } //nastavime hodnotu filtru //ovlivni to jak se bude chovat analyza //pri jakekoliv hodnote != se bude venovat //konkretnimu spojeni void Pochytane_Ramce::setFilter(int i) { filter=i; } //pro hodnotu 1 zapne zapis do souboru //pro hodnotu 2 vypisuje data na obrazovku //program povoli zapnout jen pri zadani spojeni void Pochytane_Ramce::setOutputType(const int &i) { output_type=i; } void Pochytane_Ramce::restart() { //pro jistotu, pokud neni soubor uzavren, zavri if (fhandle!=NULL) pcap_close(fhandle); //a hezky znovu otveri (pohodlne budeme na zacatku) if ( ( fhandle = pcap_open_offline(fname, NULL) ) == NULL) { fprintf(stderr,"\nUnable to find input file.\n"); return; } } //vypise IP adresu z PCAPove struktury void Pochytane_Ramce::vypis_ip_adresu(const struct in_addr &ip) const { printf("%d.", ip.S_un.S_un_b.s_b1); printf("%d.", ip.S_un.S_un_b.s_b2); printf("%d.", ip.S_un.S_un_b.s_b3); printf("%d", ip.S_un.S_un_b.s_b4); } //presune ukazatel v souboru na dalsi paket //(z dalsiho ethernetoveho ramce) bool Pochytane_Ramce::nextPacket() { //prestavi hodnotu naseho ukazatele na aktualni paket return 0!=(int)(aktualni_ramec=pcap_next(fhandle, &header)); } //presune ukazatel na aktualni eth. ramec souboru //na dalsi ramec s IP paketem bool Pochytane_Ramce::nextIPPacket() { //budeme prochazet ramce jeden po druhem //dokud v souboru nejake budou ramce //tj. next nastavi dalsi paket nextPacket(); while (aktualni_ramec!=NULL) { //a zastavime se pokud nelezneme nejaky s typem IP if (ETH(aktualni_ramec)->ether_type==TYPE_IP) break; nextPacket(); } //diky next paketu //je jiz ukazatel nastaven //vrati nulu pokud se nezdarilo return 0!=(int)aktualni_ramec; } //nastavi ukazatel akutalniho paketu //na prvni dalsi ramec s TCP paketem bool Pochytane_Ramce::nextTCPPacket() { //prochazime vsechny ramce, //zahazujeme ty, ktere nemaji IP paket nextIPPacket(); while (aktualni_ramec!=NULL) { //z dneho ramce si vydloubneme IP paket //a zkontrolujeme hodnotu promenne protokol //delame dokud to neni TCP if (IP(aktualni_ramec)->ip_p==PROTOCOL_TCP) break; nextIPPacket(); } //vratime 0 pokud se nezdarilo return 0!=(int)aktualni_ramec; } //hledame dalsi paket naseho TCP spojeni //ocekavame, ze momentalni aktualni paket je z naseho spojeni bool Pochytane_Ramce::nextConnectionPacket(void) { //neni li zadny aktualni paket chyba --> neuspech if (aktualni_ramec==NULL) return 0; //z daneho eth. ramce vyrobime IP paket a ulozime si obe adresy //(fakticiky to znamena jen prskupit bitiky do promennych) in_addr ip_src = IP(aktualni_ramec)->ip_src; in_addr ip_dst = IP(aktualni_ramec)->ip_dst; //z daneho ramce vyrobime TCP paket a ulozime si oba porty u_short port_src = TCP(aktualni_ramec)->tcp_sport; u_short port_dst = TCP(aktualni_ramec)->tcp_dport; //nalezneme dalsi TCP paket nextTCPPacket(); while (aktualni_ramec!=NULL) { if ((ip_src.S_un.S_addr==IP(aktualni_ramec)->ip_src.S_un.S_addr && port_src==TCP(aktualni_ramec)->tcp_sport && ip_dst.S_un.S_addr==IP(aktualni_ramec)->ip_dst.S_un.S_addr && port_dst==TCP(aktualni_ramec)->tcp_dport) || (ip_src.S_un.S_addr==IP(aktualni_ramec)->ip_dst.S_un.S_addr && port_src==TCP(aktualni_ramec)->tcp_dport && ip_dst.S_un.S_addr==IP(aktualni_ramec)->ip_src.S_un.S_addr && port_dst==TCP(aktualni_ramec)->tcp_sport)) break; //pokud nesedi vsechny porty a adresy, hledame dal //nextIPPacket(); nextTCPPacket(); } //nyni je ukazatel na aktualni paket naplnen ukazatelem na dalsi paket naseho spojeni //pokud je roven 0, nalezeni dalsiho paketu se nezdarilo. return 0!=(int)aktualni_ramec; } //projde vsechny ethernetove ramce a poznamena si udaje o nich //vypise na obrazovku void Pochytane_Ramce::prozkoumej_zacycena_data(void) { //nemuzeme analyzovat ramce, pokud mame filtr IP spojeni! //(analyza ramcu vyzaduje znovu nacteni souboru... //pri znalosti spojeni by to asi uz nebylo to co chceme). if (filter) return; //citace... :-) unsigned int broadcast=0; unsigned int unicast=0; unsigned int ramcu=0; unsigned int p_ip=0; unsigned int p_arp=0; unsigned int p_rarp=0; unsigned int p_others=0; //znovu otevri soubor se zachycenymi daty restart(); //prochazej ethernetove ramce while (nextPacket()) { //celkovy pocet ramcu ramcu++; //podivej se na kzady ramec (rozsekej byty do nazvanych promennych if (ETH(aktualni_ramec)->ether_dhost[0] & 1) //pokud byl posledni byt MACu 0, je to broadcast broadcast++; else unicast++; //a poznamenej si typ switch (ETH(aktualni_ramec)->ether_type) { case TYPE_ARP: p_arp++; break; case TYPE_IP: p_ip++; break; case TYPE_RARP: p_rarp++; break; } } //vypis, co jsi nalezl. printf("celkem %4d ramcu\n\n", ramcu); printf("celkem %4d unicastu\n", unicast); printf("celkem %4d broadcastu\n\n", broadcast); printf("celkem %4d IP paketu\n", p_ip); printf("celkem %4d ARP paketu\n", p_arp); printf("celkem %4d RARP paketu\n", p_rarp); } //spocitej pocet navazanych TCP spojeni //v zachycenych datech int Pochytane_Ramce::navazanychSpojeni(void) { int spojeni=0; //prochazime od zacatku restart(); //projdi vsechny IP pakety //nextIPPacket() while (nextTCPPacket()) { //a poznamenej si, pocet tech, ktere prvne vysilaji syn //- nepocitecj odpoved an sync (snyc+ack) // (potvrzeni sync ack jde jiz jen s ACK) // -- todo: zatim se fakticky pocitaji vsechny pokusy o navazeni spojeni // neoveruje se uspesnost if ((TCP(aktualni_ramec)->tcp_flags & TH_SYN) && !(TCP(aktualni_ramec)->tcp_flags & TH_ACK)) spojeni++; } //vrat pocet return spojeni; } //pro TCP spojeni s cislem (interni cislo spojeni v souboru) //nalezni jeho prvni paket a nastavi na nej aktualni_ramec unsigned int Pochytane_Ramce::naZacatekSpojeni(int i) { //zacni od zacatku restart(); //vypiseme kolikaty to byl ramec unsigned int ramcu=0; while (i) { ramcu++; nextTCPPacket(); //prochazej TCP pakety, pokud je to uvuzujici paket, odecti - 1 //tim skoncis s ukazatelem na aktualni paket nastavenym na paket //s tolikatym spojeni - kolik bylo v argumentu if ((TCP(aktualni_ramec)->tcp_flags & TH_SYN) && !(TCP(aktualni_ramec)->tcp_flags & TH_ACK)) i--; } return ramcu; } //vypise na obrazovku seznam TCP spojeni navazanych v souboru s podrobnostmi //pokud je zadany filtr - vypise informace o konkretnim spojeni void Pochytane_Ramce::prehledTCP(void) { //zjisti kolik jich bylo int spojeni = navazanychSpojeni(); //zarve, pokud chceme informace o spojeni, ktere v souboru neni if (filter>spojeni) { printf("V souboru je pouze %d spojeni.\n", spojeni); return; } //pokud neni filtr, vypise vsechny if (!filter) { //pro vsechna nalezena spojeni for (int i=1; i<=spojeni; i++) { naZacatekSpojeni(i); printf("\n%d. spojeni\n---------------------------------------\n", i); printf("Source: "); vypis_ip_adresu(IP(aktualni_ramec)->ip_src); //musime osetrit ze intel pouziva LE, zatimco podle definice IP pozuiva BE //huh! printf(" port %d\nDestination: ", ushortReverse(TCP(aktualni_ramec)->tcp_sport)); vypis_ip_adresu(IP(aktualni_ramec)->ip_dst); printf(" port %d\n", ushortReverse(TCP(aktualni_ramec)->tcp_dport)); } //jinak jen konkretni } else { unsigned int ramec = naZacatekSpojeni(filter); printf("\n%d. spojeni\n---------------------------------------\n", filter); printf("zacinam od %4d ramce\n", ramec); printf("Source: "); vypis_ip_adresu(IP(aktualni_ramec)->ip_src); printf(" port %d\nDestination: ", ushortReverse(TCP(aktualni_ramec)->tcp_sport)); vypis_ip_adresu(IP(aktualni_ramec)->ip_dst); printf(" port %d\n", ushortReverse(TCP(aktualni_ramec)->tcp_dport)); analyzujSpojeni(); } } //zjistuje, zda se menila velikost okna (aktualni: window) o proti predchozimu - packetwindow //promenna window je zmenena void Pochytane_Ramce::priviraniOkna(u_short &window, const u_short &packetwindow) { if (window!=packetwindow) { if (window<=packetwindow) printf("[priv. okna o (%d)] ", window-packetwindow); else printf("[otev. okna o (%d)] ", window-packetwindow); window=packetwindow; } } //vypise flagy TCP ramce - na obrazovku prehledne void Pochytane_Ramce::vypis_tcp_flagy() const { if (TCP(aktualni_ramec)->tcp_flags & (TH_SYN | TH_ACK | TH_FIN | TH_RST) || (IP(aktualni_ramec)->ip_off & (IP_DF | IP_MF))) { printf("["); if (TCP(aktualni_ramec)->tcp_flags & TH_SYN) printf("SYN "); if (TCP(aktualni_ramec)->tcp_flags & TH_FIN) printf("FIN "); if (TCP(aktualni_ramec)->tcp_flags & TH_RST) printf("RST "); if (TCP(aktualni_ramec)->tcp_flags & TH_ACK) printf("ACK"); if (IP(aktualni_ramec)->ip_off & IP_DF) printf("don't fragment "); if (IP(aktualni_ramec)->ip_off & IP_MF) printf("more fragments "); printf("] "); } } //prevraceni 32 bit cisla u_int Pochytane_Ramce::uintReverse(const u_int &i) const { int r=0; r = (u_int)(i & 0xFF000000) >> 24; r |= (u_int)(i & 0x00FF0000) >> 8; r |= (u_int)(i & 0x0000FF00) << 8; r |= (u_int)(i & 0x000000FF) << 24; return r; } //prevraceni 16bit cisla u_short Pochytane_Ramce::ushortReverse(const u_short &i) const { int r=0; r = (u_short)(i & 0xFF00) >> 8; r |= (u_short)(i & 0x00FF) << 8; return r; } //vypise sipku podle smeru (pomocna funkce pro dalsi) //nic zajimaveho void Pochytane_Ramce::vypis_sipecku(const int &direction) const { if (direction) printf("-> "); else printf("<- "); } //vyspise seqencni cislo paketu //pokud i potvrzuje, tak i cislo potvrzovane void Pochytane_Ramce::vypis_seq_a_ack(const u_int &seq, const u_int &ack) const { printf("seq:%0.10u ", seq); if (TCP(aktualni_ramec)->tcp_flags & TH_ACK) printf("ack:%0.10u ", ack); } //analyzuje HTTP //Datovou cast aktualniho TCP paketu void Pochytane_Ramce::analyzujHTTPPacket(void) { //tahkle kratky paket nas nezajima if (TCPDATALEN<10) return; #ifdef S_DEBUG printf("analyzuji HTTP paket, data size %d.\n", TCPDATALEN); #endif //potrebujeme sehant adresu zacatku HTTP casti v TCP (data TCP) //to je celkem jednoduche: //k aktualnimu ukazateli na zacatek ramce pricteme velikosti ETH a IP halvicky a offset z TCP paketu //const u_char *ptr = aktualni_ramec+sizeof(mask_ethernet) + sizeof(mask_ip) + TCP(aktualni_ramec)->tcp_off*4; const u_char *ptr = aktualni_ramec + sizeof(mask_ethernet) + (IP(aktualni_ramec)->ip_hl*4) + (TCP(aktualni_ramec)->tcp_off*4); //( ushortReverse(IP(aktualni_ramec)->ip_len) - IP(aktualni_ramec)->ip_hl*4 - TCP(aktualni_ramec)->tcp_off*4 ) -24 //vytvorime si sadu pomocnych ukazatelu a pameti pro data char *p = new char[TCPDATALEN+10]; char *p1 = p; char *p2 = new char[TCPDATALEN+10]; char *p3 = new char[TCPDATALEN+10]; char *p4 = new char[TCPDATALEN+10]; int statuscode = 0; int fileformat=0; #define GIF 1 #define JPEG 2 //zkopirujeme aktualni data z TCP paketu do p //strcpy blbne!!!! //strncpy(p,(char*) ptr,TCPDATALEN); //problem, na zacatku muze byt pekny bordel!!!! //kde se tam vzal, asi je neco spatne! :-( //zmrvi srovnani! //brutalne osetrime! ;-X //zjistime kde swe vyskytuje prvni pismenko.... int bordel_counter = 0, bordel_pozice = 0; while((bordel_counter < TCPDATALEN) && ((bordel_counter < 24) || (ptr[bordel_counter] <= 32) || (ptr[bordel_counter]>=127))) { if ((ptr[bordel_counter] <= 32) || (ptr[bordel_counter]>= 127)) bordel_pozice = bordel_counter; bordel_counter++; } bordel_pozice = 20; memcpy(p,(const char *)(ptr + bordel_pozice), TCPDATALEN-bordel_pozice); //osetrime si, ze na konci je 0 p[TCPDATALEN]=0; //debug #ifdef S_DEBUG printf("##bp:%d\n",bordel_pozice); printf("####ptr:"); for (int tmp_d = 0; tmp_d < TCPDATALEN; tmp_d++) fputchar((ptr)[tmp_d]); printf("####\n"); #endif if(output_type == 2) { printf("TCP data(%db):\n===========================\n", TCPDATALEN); for (int tmp_d = 0; tmp_d < TCPDATALEN+10; tmp_d++) fputchar((p)[tmp_d]); printf("\n===========================\n"); } /* //vydloubneme si prvnich par bytu bytu //strncpy(p2, p, 7); //blbne!!!!!! memcpy(p2 , p, (TCPDATALEN<30)?TCPDATALEN:30); //opet osetrime 0 na konci p2[(TCPDATALEN<30)?TCPDATALEN:30]=0; #ifdef S_DEBUG printf("####p2:%s####\n",p2); #endif */ //tyka se nas to, je to skutecne HTTP? if (strstr(p, "HTTP/1.") != NULL) { #ifdef S_DEBUG printf("\n!!!!! Nalezl jsem HTTP hlavicku, slintam blahem!\n"); #endif //nacteme se si ze zacatku paketu status kod //sscanf zajisti i prevod cisla na integer int i=0; sscanf(p, "%s %d", p2, &i); //zacneme si do HTTP buferu ukladat informace o pochytanych datech HTTPBufferPtr+=sprintf(HTTPBufferPtr, "%s, Status Code: ", p2); //s podle koduu zjistime co se deje //zapiseme si switch (statuscode=(i/100)) { case 1: HTTPBufferPtr+=sprintf(HTTPBufferPtr, "Information"); break; case 2: HTTPBufferPtr+=sprintf(HTTPBufferPtr, "Successful"); break; case 3: HTTPBufferPtr+=sprintf(HTTPBufferPtr, "Redirection"); break; case 4: HTTPBufferPtr+=sprintf(HTTPBufferPtr, "Client error"); break; case 5: HTTPBufferPtr+=sprintf(HTTPBufferPtr, "Server error"); break; } HTTPBufferPtr+=sprintf(HTTPBufferPtr, ", "); int isum=0; while(1) { //p1 ukazuje tam kam p, tj tam kam byla naladovana data //z data segmentu TCP paketu //preskocime prvni dva radky //ale prekopirujeme do do bufferu //precteni radku (z p1 do p2, posun v p1) int i=StrCSpn(p1, "\n"); isum+=i; strncpy(p2, p1, i); p2[i]=0; p1+=i+1; if (i==0) break; //treti radekcek si ohodime do p3 i=StrCSpn(p2, " \n"); if (i==0) break; strncpy(p3, p2, i); p3[i]=0; //rika tento radek neco o content type? if (strcmp(p3, "Content-Type:")==0) { //pokud ano, prozkoumame sscanf(p2, "%s %s", p3, p4); //pozice stredinku - ten zakoncuje //radeji nahradime 0, jistota.... if (p4[strlen(p4)-1]==';') p4[strlen(p4)-1]=0; HTTPBufferPtr+=sprintf(HTTPBufferPtr, "Content-Type: %s", p4); //pro sve potreby si ulozime informace o formatu if (strcmp(p4, "image/gif")==0) fileformat=GIF; } } //status code - tan z HTTP hlavicky 200 je OK. //takze pokud bylo spojeni v poradku a nahodou je to obrazek gif if (statuscode==2 && fileformat==GIF) { int x, y; p1=(char*)(aktualni_ramec+54+isum); while (*p1!='G') p1++; //for (i=0; iseq) && (TCPDATALEN>0)) { printf("[opak. paket] "); return 1; } return 0; } //analyzuje aktualni spojeni //aktualni spojeni je dano tim ze je aktualni ramec nastaven na jeho zacatek void Pochytane_Ramce::analyzujSpojeni(void) { //zjistime si zdrojovou IP (format PCAPU) in_addr ip_src = IP(aktualni_ramec)->ip_src; //a zdrojovy port, ah Intele, proc zrovna LE? //tento program by na Itaniu nechodil. int port_src = ushortReverse(TCP(aktualni_ramec)->tcp_sport); //prommenne pro hlidani okenka th_seq src_awaiting = 0; u_short src_window = 0; th_seq dst_awaiting = 0; u_short dst_window = 0; //citace... int cntOpakovanych = 0; int cntCelkem = 0; int cntSyn = 0; int cntFin = 0; //osetrime ze je v bufferu prvni znak 0, pro operace s retezci HTTPBuffer[0]=0; //a jeho adresa.... //s jeji pomoci budeme pak sledovat, jak se pohybujeme HTTPBufferPtr=HTTPBuffer; //a budeme delat //(zastaveni podle obsahu paketu vevnitr while (1) { //pro kazdy paket cntCelkem++; //zjistime si jeho smer //to pozname snadno, je nase ulozena zdrojova IP zdrojova nebo cilova? //od nas = true (//true=indir.) bool direction = ip_src.S_un.S_addr==IP(aktualni_ramec)->ip_src.S_un.S_addr && port_src==ushortReverse(TCP(aktualni_ramec)->tcp_sport); //zjisitme si seq a ack cisla z aktualniho paketu u_int tcp_seq = uintReverse(TCP(aktualni_ramec)->tcp_seq); u_int tcp_ack = uintReverse(TCP(aktualni_ramec)->tcp_ack); //pokud paket obsahuje datovou cast, vydloubneme ji //a ulozime ji do listu pro pozdejsi uziti (cislovani pomoci seq umozni snadne uziti listu if (TCPDATALEN>0) { //struktura pro ulozeni dat z data segmentu //(vlastne jen pointer na data a seq cislo) datasegment ds; //pointr na zacatek TCP datove casti const u_char *data_ptr = aktualni_ramec+sizeof(mask_ethernet) + sizeof(mask_ip) + TCP(aktualni_ramec)->tcp_off*4; //misto pro data v nasi strukture ds.data=new char[TCPDATALEN+10]; //naplni me ji //cislem ds.seq=tcp_seq; //daty memcpy(ds.data,(const char *)data_ptr,TCPDATALEN); //osetrime zaverecnou 0 ds.data[TCPDATALEN]=0; ds.velikost=TCPDATALEN; //podle smeru zatridime do listu if (direction) stream_out.push_back(ds); else stream_in.push_back(ds); //pripadne vyplivneme na obrazovku /* if (output_type == 2) { printf("TCP data(%db):\n===========================\n", TCPDATALEN); for (int tmp_d = 0; tmp_d < TCPDATALEN; tmp_d++) fputchar((ds.data)[tmp_d]); printf("\n===========================\n"); } */ //debug #ifdef F_DEBUG for (int tmp_d = 0; tmp_d < TCPDATALEN; tmp_d++) fputc(((ds.data)[tmp_d]),fd); #endif } /////desifrujeme flagy //RESET, tak to je celkem jednoduche if (TCP(aktualni_ramec)->tcp_flags & TH_RST) printf(" spojeni ukonceno resetem!\n"); else { if (TCP(aktualni_ramec)->tcp_flags & TH_FIN) //FIN Flag, uz jsme nejakou videli //jestli ne, je to zacatek ukoncovani (ta prvni) if (!cntFin) printf(" zacatek ukoncovani spojeni\n"); //jinak jsme to my, kdo ji uvidi else printf(" ukoncovani spojeni z \"druhe strany\"\n"); //pokud dostaneme sync (a neni s ACK) navazujeme spojeni //(sync s ack potvrzeni navazani) //(ack samoty - 3 faze handshaku) if (TCP(aktualni_ramec)->tcp_flags & TH_SYN && !(TCP(aktualni_ramec)->tcp_flags & TH_ACK)) printf(" zacatek navazovani spojeni\n"); } //graficky vyvedeme falgy na obrazovku vypis_sipecku(direction); vypis_seq_a_ack(tcp_seq, tcp_ack); vypis_tcp_flagy(); //vypise dalsi info: //velikost okna, ehhhh, ehhh ~0xFFFF)+1 printf("data:%4db, win:%5u ", TCPDATALEN, (ushortReverse(TCP(aktualni_ramec)->tcp_win))); //blizsi rozbor zacatku //sync flag maji prvni paket a odpoved na nej if (TCP(aktualni_ramec)->tcp_flags & TH_SYN) { //podel smeru paketu //jsem-li ja zdroj //navrhuji velikost okenka if (direction) { src_window = ushortReverse(TCP(aktualni_ramec)->tcp_win); src_awaiting=tcp_seq+1; printf("(src. window=%d) ", src_window); } else { //velikost okenka navrzena cilem dst_window=ushortReverse(TCP(aktualni_ramec)->tcp_win); dst_awaiting=tcp_seq+1; printf("(dst. window=%d) ", dst_window); } } //opakuje se paket, to poznam snadno //pmamtuji si, na jaky seq cekam //a pokud tento paket co mi prisel ma nizsi seq nez //je to ktere cekam, musi se opakovat (nebo je prehozeny) //POZOR PAKET S DATY = 0 NENI OPKOVANY - JE JEN KVULI ACK if (!(TCP(aktualni_ramec)->tcp_flags & TH_SYN)) cntOpakovanych+=opakovanyPaket(tcp_seq, direction?dst_awaiting:src_awaiting); //kontroluji jak to je s velikosti okenka //pokud se okenko od posledniho paketu nezmenilo, nevypise ani carku priviraniOkna(direction ? src_window: dst_window, ushortReverse(TCP(aktualni_ramec)->tcp_win)); //kontrolovani seq cisel //vime ktere seq ocekavame (kazdy byte +1) //nezajimaji nas syn pakety. if (!(TCP(aktualni_ramec)->tcp_flags & TH_SYN)) { //nejprve kontrolujeme smer if (direction) { //pokud jsme v tomto paketu dostali seq //ktere jsme cekali po minulem //tak nastavime nove ocekavane seq na soucasne + delka dat if (tcp_seq >= src_awaiting) src_awaiting = tcp_seq+TCPDATALEN; } else { //cil si zkontroluje zda dostal spravne seq - navazujici na to ktere cekal //pokud ano, upravi ocekavani if (tcp_seq >= dst_awaiting) dst_awaiting = tcp_seq+TCPDATALEN; } } //zacitej si flagy if (TCP(aktualni_ramec)->tcp_flags & TH_SYN) cntSyn++; if (TCP(aktualni_ramec)->tcp_flags & TH_FIN) cntFin++; if (TCP(aktualni_ramec)->tcp_flags & TH_RST) cntFin += 2; printf("\n"); //predpokladame, ze HTTP bezi na 80ce //pokud tomu tak je, predame pake k analyze //ta by z nej mela predevsim vydloubat informace o datech a http if (ushortReverse(TCP(aktualni_ramec)->tcp_sport)==80) // predpokladam HTTP analyzujHTTPPacket(); //kdyz se nezdari najit dalsi paket spojeni, koncime //jinak nam tato operace prehodi pointer aktualnigho ramce na dalsi ramec s //TCP paketem naseho spojeni if (!nextConnectionPacket()) break; } ///////////////////////////////// //a vypiseme finalni statistiku// //pokud nam analyza HTTP paketu neco naplnila do bufferu //tak to z nej vypiseme! //yppie! if (HTTPBuffer!=HTTPBufferPtr) printf("---------------------------------------\n%s", HTTPBuffer); // printf("---------------------------------------\n"); printf("Celkem zachyceno paketu: %d\n", cntCelkem); if (cntOpakovanych>0) printf("Z toho opakovanych paketu: %d\n", cntOpakovanych); //pokud nebylo spojeni ukonceno regulerne dvojitym potvrzenim //zarveme if (cntFin<2 || cntSyn<2) printf("Zaznam spojeni neni kompletni!\n"); printf("---------------------------------------\n"); //pokud byl zapnuty file output //zapiseme zachycena data do souboru //data od nas a od zdroje if (output_type == 1) { //nejprve si listiky setridime podle seq cisel //data budou hezky vp oradi stream_in.sort(); stream_out.sort(); //datasegment ds; //vyplivneme do souboru FILE *fh = fopen("analyza1.txt", "wb"); while (!stream_in.empty()) { const datasegment ds=stream_in.front(); stream_in.pop_front(); for (int tmp_d = 0; tmp_d < ds.velikost; tmp_d++) fputc(((ds.data)[tmp_d]),fh); //fprintf(fh, "%s", ds.data); } fclose(fh); //napodobne... fh=fopen("analyza2.txt", "wb"); while (!stream_out.empty()) { const datasegment ds=stream_out.front(); stream_out.pop_front(); for (int tmp_d = 0; tmp_d < ds.velikost; tmp_d++) fputc(((ds.data)[tmp_d]),fh); //fprintf(fh, "%s", ds.data); } fclose(fh); } } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////MAIN////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { if (argc>=2) { //viz. definice - struktura kam se nactou vsechny zachycene IP pakety //jmeno souboru s NETDUMPem Pochytane_Ramce *ef = new Pochytane_Ramce(argv[1]); //pokud byl zadan treti parametr, nastav filtr if (argc>=3) ef->setFilter(atoi(argv[2])); //+ pokud byl zadan jakykoliv 4 prametr, zapne vystup do souboru if (argc>=4) ef->setOutputType(atoi(argv[3])); //co s tady bude provadet zavisi hlavne na hodnotach vyse ef->prozkoumej_zacycena_data(); ef->prehledTCP(); //konec delete ef; }else { printf("Programek Analyza pro reseni ulohy 1, verze 3, 36LOS na FEL-CVUT\n"); printf("pouziti: analyza.exe EthDumpfile [n [m]]\n", argc); printf(" EthDumpfile .. datovy soubor s dumpem provozu - obousmernym\n"); printf(" n ............ cislo spojeni o kterem chceme dalsi\n"); printf(" informace, zjistem z vypisu programu.\n"); printf(" m ............ 1: zapis prenasenych dat do souboru:\n"); printf(" analyza1.txt (od zdroje) a analyza2.txt (od cile)\n"); printf(" m ............ 2: zapis prenasenych dat na obrazovku\n\n"); } return 0; }