Mega Code Archive

 
Categories / Delphi / Forum
 

Neoturk - tcp-ip ve nmstrm analizi

herkese merhaba, teknik analizlerimden nostaljiler.............. ----------------------------------------------------------------- Written by neoturk - 02.01.2004 Konu: TCP/IP protokolu üzerinden dosya transferi ( kendi buluşlarım… ) Merhaba ortak… Birkaç haftadır kafamı meşgul eden dosya transferinin nasıl olacağı konusunda epey bir teorik düşünce sarfettim.. kendi yazdığım mini bir programda tcp/ip üzerinden dosya transferini yaptım. Local olarak %100 olarak çalışıyor, ama kafamda hala cevaplanmamış sorular var, biraz da ben abartıyorum, çünkü tekli dosya transferinden ziyade multi-transfer olayının olmasını istiyorum. Bunun yanı sıra multi-transfer olurken multi-connect işlemini de desteklemesini istiyorum. Teoride biraz zor görünüyor, pratikte biraz uğraştırır. Kafayı takarsam yaparım diyorum ama şu an için gereği yok.. zaten böyle bir şeyi yaptığımda kazaa türü program yapmış oluyoruz.. Herneyse, kodu ben de incelemek istiyorum. Yerine oturtmam gereken kodlama ve düşünsel mantıklarım var. Ne zamandır analizleyememiştim şimdi başlıyoruz…. Basit bir client arabirim ve basit bir server arabirim programı yazmıştım, içinden kritik kodları çektim ve tek tek analiz etmek istedim. Böylece senin de fikirlerini almış olurum, daha iyi irdelemiş oluruz.. Öncelikle vurgulamak istediğim çok önemli analizler var: 1. Internet programcılığı ile uğraşıyor isek ( bu trojan veya her neyse fark etmez ) kesinlikle ve kesinlikle DOSYA TRANSFERİ olayının %100 sorunsuz olarak çözülmesi gerekiyor. Çünkü bağlantı sağlanmasında bir problem yok, ancak er ya da geç dosya transfer olayına ihtiyaç duyulacak! Bunun için bu konunun bizzat çözümlenmesi ve kendi mantığımıza göre işleme geçmesi gerekiyor. 2. Delphi olsun CBuilder olsun, bu programlama dillerinde dosya transfer işini gören NMSTRM adlı komponent (paket) serisi var. Hemen hemen tüm programcılar bu komponentleri kullanırlar. ( Çok önemli dip not: Okuduğum birçok delphi kitabında (okumadığımı yok denecek kadar parmakla gösterebilirim) dosya transfer örneklerini nedense hep nmstrm komponentleri üzerinde anlatırlar. Oradaki örneklerde dosya transferleri çalışır. Oturur yazarsın ve her şeyin çalıştığını görünce bir şeyler öğrendiğini sanırsın.. doğrudur.. ama Level2'ye geçtiğinde NMSTRM komponentlerinin yetersiz olduğunu ben dahil deneyimli coderler er geç fark etmişlerdir… Bu nedenle oturup adam gibi kodunu kendin yazmalısın.. ( icq gibi, paltalk gibi.. ) 3. Örnek trojanlar incelendiğinde nedense çoğunda dosya transfer olayının nasıl yapıldığı gösterilmemiştir. Bunu gösteren tek örnek trojan kodu LATINUS trojanıdır. ( tek tük bazı trojanlarda da mevcut ancak tarihi eser kalıntılarıyla dolu ve çalışmayan kod parçacıklarıdır ) 4. Latinus trojanındaki dosya transfer işlemi Multi-File yöntemini destekleyen şekilde transfere müsaade ediyor, ama kullanımı pek etkin değil.. denedim kullandım çalıştırdım ama etkin değil.. hangi dosyanın ne zaman vardığı, nereye gittiği Allah kerim.. gidiyor dosyalar ve direkt temp klasörüne kopyası çıkartılıyor.. kodu yazan adam biraz kulağını SAĞ'dan göstermiş.. welhasıl pek verimli bir kod değil… 5. Dosya göndermek gerçekten önemli bir konu, nedenine gelince, coderlerin %99'u ( abartmıyorum ) nmstrm komponentini kullanırlar ve kullanacaklardır. Oturup sıfırdan kodunu yazması inan kasıyor… işin komik yanı da network içinden dışarıya bağlantı yapıldığında nmstrm componentleri dosya transferlerini yapamıyor.. bunu ya bilerek es geçtiler ya da unuttular.. çünkü bizim daha önceki yazdığımız tüm versiyonlarda nmstrm komponentlerini kullanmıştım. O yüzden network-dialup arasındaki dosya transferleri olmuyordu. ( network içinden evdeki pcye bağlantı örneğinde olduğu gibi ) ( ama msn veya icq bunu gayet güzel yapıyor değil mi ? ) 6. Çok ince bir nuans daha var, tcp/ip ( socketler ) üzerinden dosya göndereceğimiz zaman 2 seçeneğimiz var. Block ve None-Block seçimi… block işlemli socketlerde bir sonraki gelen veri paketi cache socket belleğinde tutulur. Aktif olan veri dosyaya yazıldıktan sonra cache socket belleğinden alır ve böylece işlemler devam eder.. ( winsock topolojisi ). none-block seçimi yaparsak cache bellekte veri tutulmaz. Gelen veri direkt işlenir. İşlenen bir veri sırasında şayet ikinci bir veri paketi gelirse bu paket boşa gide çünkü cache bellekte tutulmaz… burada şöyle bir sorun var, winsock cache bellek dolar elbet… bu durumda da aynen 3ün 1ini alırız… bu sorunun düzeltilmesi konusundaki bazı profesyonel yaklaşımları birkaç programda görmüştüm.. bunlardan en etkini de bildiğimiz icq ve msn dosya transferleri… 7. Benim amacım cache belleğin dolmasına müsaade etmeden ping-pong ( paketi aldım-sonrakini gönder) mantığına göre veri paketlerini sırayla göndermek. ( icqdaki file send speed olayı, 1-5 arası hız seçimi vardı hatırlarsan.. olayın özü burada yani… ) Şimdi ping-pong işlemine dayalı dosya transferi yapan programı inceleyelim : CLIENT PROGRAMI : DosyaGönder tuşuna tıklandığında aşağıdaki kod yürürlüğe giriyor: Procedure button1.onclick(sender:Tobject); Const bufsize=2048 Var x:string; Size,fsize,bytesent:longint; Fsrc,len: integer; Begin Stepped:=0; İf filelistbox1.itemindex=-1 then Begin Memo1.lines.add('select file..'); Exit; End; X:=directorylistbox1.directory+'\'+filelistbox1.items[filelistbox1.itemindex]; Memo1.lines.add(x); İf clientsocket1.socket.connected=false then Begin Memo1.lines.add('connect?'); Exit; End; Fsrc:=fileopen(x,fmopenread); Fsize:=fileseek(fsrc,0,2); fileseek:=fileseek(fscr,0,0); gauge1.maxvalue:=fsize; memo1.lines.add(inttostr(fsize)+' byte…'); memo1.lines.add('uploading…'); sendfilegate1:=true; bytesent:=0; size:=fsize; stopped:=false; while size>0 do begin if kapat then break; application.processmessages; if (sendfilegate1) and (stopped=false) then begin len:=fileread(fscr,buffer,sizeof(buffer)); memo1.lines.add(inttostr(len)); clientsocket1.socket.sendbuf(buffer,len); gauge1.progress:=bytesent; bytesent:=bytesent+len; size:=size-len; sendfilegate1:=false; end; fileclose(fscr); memo1.lines.add('upload ok'); gauge1.progress:=0; end; CLIENT SOCKET READ Procedure clientsocket1read(sender:Tobject); Var dta:string; Begin Dta:=trim(socket.receivetext); İnc(stepped); Form1.caption:=dta+'/'+inttostr(length(dta))+'='+inttostr(stepped); İf dta='pong1' then sendfilegate1:=true End; SERVER PROGRAMI: … Const bufsize=2048; Var Form1:Tform1; Fscr,fdst,len:integer; Size:longint; Buffer: packed array [0..bufsize-1] of byte; …. Procedure Tform1.formcreate Begin Fdst:=filecreate('c:\deneme.txt'); End; Procedure form1.serversocket1clientread; Var dta:string; Begin Len:=socket.receivebuf(buffer,bufsize); Filewrite(fdst,buffer,len); Memo1.lines.add('bufbyte='+inttostr(len)); Socket.sendtext('pong1'+#13#10); End; Server programını analiz edelim : //Genel tanımlamalar … Const bufsize=2048; //bufsize değerini 2048 olarak atadım, bunu 4096 byte'a çıkarabiliriz.. maximum 8192 byte yapıyor.. bu da icqdaki dosya gönderme hızıyla doğru orantılı bir mantık.. her seferde kaç byte veri alacağını bu değer tutuyor Var Form1:Tform1; Fscr,fdst,len:integer; //fscr(file source): clientten gönderilen dosya parçacığı //fdst(file destination): server programının okunan verileri nereye yazacağı //len: okunan veri byte miktarı Size:longint; //size: dosyanın uzunluğu ( longint = 2GB veriye kadar destekliyor,teori ) Buffer: packed array [0..bufsize-1] of byte; //buffer: okunan veri paketi. Packet olduğunu belirtiyoruz ( "packed" deyimini bulana kadar canım çıktı : güldüğüme bakma, packed yazmazsan açılan buffer sıradan bir array olur… packed ön eki ile harbiden paket olduğunu söylüyoruz.. tcp/ip dünyasına hoş geldin bebek! :p ) …. //program ilk çalıştığında Procedure Tform1.formcreate Begin Fdst:=filecreate('c:\deneme.txt'); //gelen verileri hangi dosyaya yazacağını söylüyoruz.. bu biraz hızlı işlem oldu. Uzun vadede tabii ki nereye upload ediliyorsa oraya yazması gerekiyor. Onu hallederim. Şimdilik tek amacımız veri paketleri adam gibi gitsin ve oraya yazılsın. Dimi ? : End; //server herhangi bir paket algıladığında Procedure form1.serversocket1clientread; Var dta:string; Begin Len:=socket.receivebuf(buffer,bufsize); //gelen paketi buffer dizisine aktar, bufsize kadar. Ve uzunluğu len'dir Filewrite(fdst,buffer,len); //buffer dizisini len uzunluğu kadar FDST(deneme.txt) dosyasına yaz Memo1.lines.add('bufbyte='+inttostr(len)); //okunan paketin boyutunu yaz, kaç byte olduğunu Socket.sendtext('pong1'+#13#10); //paket dosyaya yazılmıştır, sonraki paketi istemek için PONG1 emrini cliente gönder End; CLIENT VE SERVER EKRAN GÖRÜNTÜLERİ İŞLEM YAPARKEN: ŞEKİL 1: Client programı servere bağlanmış durumda, aaaproject1.exe dosyasını upload ederken Stop ve continue tuşları ile upload işlemini istediğim zaman durdurup istediğim zaman devam ettirebiliyorum. Gönderilen dosya 490KB. Paketler 2048 byte'lar halinde gönderiliyor. Bu işlemler hızlı oluyor. Benim bilgisayarda ( p4-512 ram-xp işletim) 3-4 sn kadar bir süre alıyor.. Yukarda yazan pong1/5=175 'in anlamı, serverden pong1 verisini aldım benden sonraki paketi istiyor. Bende 173.paketi gönderiyorum.. ( 2048 x 173 = 354304 byte gönderilmiş durumda… ) işlem bittiğinde toplam 241 paket gönderilmiş olacak… ŞEKİL 2: Server programı veri paketlerini alırken Dosya upload işlemi tamamlandığında gönderilen son paket 1536Byte olduğuna dikkat edelim…. ( 2048 bytelar hesaplanıyor, son arta kalan paket 1536 byte olarak alınmış.. aynı veri clientte de mevcut zaten ) Şu mp3ü değiştiriyim yaw! Ortak bisküvi yer misin ?... CLIENT PROGRAMINI ANALIZ EDELİM: Procedure button1.onclick(sender:Tobject); //SEND tuşuna tıklandığında Const bufsize=2048 //2048 bytelar halinde paketler gönderilecek Var x:string; Size,fsize,bytesent:longint; Fsrc,len: integer; Begin Stepped:=0; //gönderilmiş olan paket miktarı stepped //bir dosya seçilmiş mi gönderilmek için İf filelistbox1.itemindex=-1 then Begin Memo1.lines.add('select file..'); Exit; End; X:=directorylistbox1.directory+'\'+filelistbox1.items[filelistbox1.itemindex]; //seçilmiş olan dosyayı X değişkeninde tut Memo1.lines.add(x); //bağlantı yapılmış mı bak İf clientsocket1.socket.connected=false then Begin Memo1.lines.add('connect?'); Exit; End; //ok, bağlantı var, başlıyorum dosyayı göndermeye, paketler halinde Fsrc:=fileopen(x,fmopenread); //x dosyasını aç bakalım … Fsize:=fileseek(fsrc,0,2); //dosyanın boyutunu al, fsize ( file size ) değişkeninde tut fileseek:=fileseek(fscr,0,0); //dosyanın başına konumlan gauge1.maxvalue:=fsize; memo1.lines.add(inttostr(fsize)+' byte…'); memo1.lines.add('uploading…'); sendfilegate1:=true; //paketi gönderme modu açık ( send file gate = dosya gönderme kapısı 1 nolu… 1-2-3-4… nolu kapılar eklediğimizde multitransfer yapabiliriz umudunu taşıyorum : ) bytesent:=0; //gönderilmiş olan toplam byte miktarı 0 dır başlangıçta size:=fsize; stopped:=false; //durdurma tuşuna basılmamıştır //başlıyoruz….. kim tutar seni !... hurraaaaaaa alalalalalalalalala !!!! while size>0 do //dosyanın sonuna kadar okumaya devam et begin if kapat then break; //acil kapatmaya izin ver. Transfer iptal edilebilir… application.processmessages; //paketleri gönderirken diğer işlemlerini de yapıver if (sendfilegate1) and (stopped=false) then //paketgönderme müsaid ise ve durdurma tuşuna basılmamış ise begin len:=fileread(fscr,buffer,sizeof(buffer)); //dosyadan konumlan ve ilgili paketi oku memo1.lines.add(inttostr(len)); clientsocket1.socket.sendbuf(buffer,len); //okuduğun paketi servere gönder gauge1.progress:=bytesent; bytesent:=bytesent+len; //gönderilmiş toplam byte miktarı size:=size-len; //geri kalan toplam byte miktarı sendfilegate1:=false; //dur.paketi gönderdin. Serverdan cevap bekle end; fileclose(fscr); //gönderdiğin dosyayla işin bitti, kapat memo1.lines.add('upload ok'); //ne mutlu türküm diyene! Upload is ok_ gauge1.progress:=0; end; //cliente serverdan veri geldiğinde CLIENT SOCKET READ Procedure clientsocket1read(sender:Tobject); Var dta:string; Begin Dta:=trim(socket.receivetext); İnc(stepped); Form1.caption:=dta+'/'+inttostr(length(dta))+'='+inttostr(stepped); İf dta='pong1' then sendfilegate1:=true //gelen veri "pong1" ise sonraki paketi gönderebilirsin, sendfilegate1 kapısını aç! End; bi sigara yakiyim……………………………… akşam bara gitcem, içmem lazım…….. şu anda saat 19:15… 21:00 gibi barda olucam… zom oluncaya kadar da içcem.. efkar bastı anasını satim……….. sevgilimiz bile yok……………………… GENEL İŞLEYİŞ DURUMU: Clientten servere herhangi bir dosya upload edileceği zaman, gönderileceği dosyanın başına konumlanıyoruz. Ve 2048 byte'lık dilimlere bölüyoruz ( bu arada aslında downloada veya uploada kaldığı yerden devam edebilir ayarını çekebilirim.. biraz kastırır ama dilim terminolojini çözdük, artık gam yemeyiz heralde… neyse, yarım kalan dosyayla filan uğraşmaya gerek yok… ) Client programı, bu dilimleri PEŞPEŞE servere göndermek ister… ama peşpeşe gönderemez… çünkü ilk dilimi gönderdiğinde karşı taraf (server), şunu der: "hop kardeş, stop.. şu veriyi yazıyım, ben sana talimat veririm sonraki veriyi göndermen için" der… Server ilgili paketi yazdıktan sonra cliente "pong1" emrini verir ( gönder kardeşim sonrakini… ) ve client (azmış durumda!) sonraki paketi de gönderir…… Böylece dosyanın sonuna kadar dilimlenmiş olan paketlerin hepsi kısa bir süre içersinde gönderilmiş olur… ( kısa bir süre dedim, benim pc için. Bir de net bağlantı hızı da söz konusu.. artık ADSL teknolojisi de var, orasını düşünmeye gerek yok artık…. ) Yani sonuç olarak, dilimler halinde dosyayı karşı tarafa gönderebiliyoruz. Bunu yaparken de diğer işlemleri de yapabiliyoruz. Dosya transferini kullanan portlara diğer işlemleri karıştırmamalıyız, yoksa veriler karışabilir… ne olur ne olmaz… sadece "pong" emri kullanıldı… Gerçek bir net bağlantısında bunu denemeyi çok isterdim, hız durumu nedir ne değildir bilmiyorum, ama normal bir icq hızında olacağı kanısındayım…. Biz 2048 bytelık paketler kullandık… bunu 512-1024-2048-4096 bytelık paketler ( HIZ OLUYOR ! ) halinde ayar çekebiliriz… 4096 dan büyük olmaz kanısındayım… veya 8192'den büyük olamaz! Çünkü maksimum bu kadar paket gönderilebiliyor tcp/ip protokolünde… ( icqda nasıl uzun mesajlar gönderilemiyorsa ve bize "YEah Right!:)" mesajı yazıyorsa, nedeni bu olduğu için yani ! ) saygılarımla_ xxnt03@lycos.co.uk neoturk_