Friday, January 6, 2012

TDL / TDSS Serisi – Analiz ve Temizlik

Geçen yazıda söz verdiğim gibi bu yazıyı TDL’in analizine ayırıyorum. Öncelikle nereden çıktı şimdi TDL Analizi sorusuna cevap olarak : yeni projemizde en ileri seviye rootkitleri dahi temizlememiz gerekiyor. İşimiz gereği yeni çıkan rootkitleri derinlemesine inceliyor ve Ar-Ge olarak gerekli tüm detayları kayıt altına alıyoruz. Yeni projenin bir çok parçasını bitidrikten sonra şimdi de sıra Storage Stack ile ilgili olan kısımlara gelmişti ki, ekip şefimiz ile konu hakkında detaylı bir görüşme yaptık ve TDL 3 ve 4’den başlamaya karar verdik. Bu yazıda temizlik için izlediğimiz adımları sizlerle paylaşacağım. İşlemin bazı yerlerinin Zemana Ar-Ge Bünyesinde kalması gerektiğinden bazı yerleri üzeri kapalı geçeceğim fakat özünde izlenmesi gereken adımları anlamak çok da zor olmasa gerek.
Öncelikle TDL’in özelliklerinden bahsedeyim :
- Şimdiye kadar görülen en kompleks ve stabil enfektör.
- Windows’un KPP korumasını atlatan ilk zararlı (Mekanizmayı anlattığımda gözlerinize inanamayacaksınız)
- Genel maksatlı bir downloader olduğu gibi bilgisayarınızı tamamen köleleştiren ve bu işlemlerin neredeyse tamamını Kernel Moddan icra eden bir zararlı.
- Dünya genelinde 5-6 Milyon’a yakın bilgisayarı enfekte ettiği tespit edilmiş.
Aşağıdaki grafiğe bakacak olursanız, 2010’daki % 600 lük artış dikkatinizi çekecektir. Zararlının 10 kişilik bir ekip tarafından yazıldığı düşünülüyor ve AntiVirüslerden daha hızlı güncelleniyor GülümsemeGülümsemeGülümseme
1
Konuya girecek olursam, ekip şefimiz iki adet sample gönderdi. Enfeksiyon mekanizmaları birbirlerine benzer olan
1. 03E059756CEAD6186B2E386FC2F8A023 hashli TDL3 örneği.
2. 4A052246C5551E83D2D55F80E72F03EB hashli TDL4 örneği.
Daha önceden okuduğum analizlerde Botun özellikle sanal makine avcısı olduğunu biliyordum. Bu yüzden özel olarak hazırladığımız sanal makinelerle test etmek istedim. Eğer test başarısız olsaydı, lab ortamında çalışan  makinelerimize “DEBUG MODE OFF” imajlarından birisini yükleyip ile deneyecektim fakat bu genellikle çok zahmetli bir iş. Çünkü, enfekte edilen makinenin devamlı olarak LiveKD / DD ile snapshotunu almak zorunda kalıyorsunuz ve geriye dönmek istediğinizde ise maalesef FOG Server sizi epey bekletiyor. Neyseki TDL3 sanal makinamı tespit edemedi Gülümseme
Zararlının çeşitli analizlerini bulmanız mümkün fakat hangi versiyonunun ne yaptığından ziyade, bizim için önemli olan yeni üründe kullanılacak Jenerik Temizleme Mekanizması. Dolayısıyla rutin prosedürlerin çoğunu uygulayıp emin olmak istedim.
Zararlıyı çalıştırıp DbgView’a baktığımda aşağıdaki görüntüyü gördüm. Karşımda bulunan adamların kesinlikle sağlam bir mizah anlayışları olduğu kesindiGülümsemeGülümsemeGülümseme
Düşünün, bulaştığınız makinelere bir debug print yapıyorsunuz ve “Uzay Yolu”ndan replikler gösteriyorsunuz. Aslında bir nevi meydan okuma da diyebiliriz. Bu arada TDL den ilk küfrümü de yemiş oldumGülümseme
2
Her zaman yaptığım şeyi yaptım ve !chkimg komutu ile ntoskrnl.exe yi kontrol ettim. Uppsss, 6 baytlık bir değişiklik, fakat maalesef bunlar DebugView programının nt!DebugPrint fonksiyonunda yaptığı patchlerdiGülümseme
3
İkinci adım olarak servis tablosuna, Global Descriptor Tablosuna ve LDT’ye baktım. Biliyorum fazla paranoyağım ama karşımdaki ekip cidden profesyonel bir ekip, dolayısıyla emin olmak çok önemli. Herneyse, paranoya adımlarını geçip, Sistem callbacklere baktığımda 0xf887b6ae adresinde 1 tane Create Process Callback olduğunu tespit ettim. İşte bu güzel haberdi. lm ile modül listesine baktığımda bu adreste geçerli bir modül olmadığını gördüm.
!pool 0xf887b6ae  komutu ile adresin base’ine göz attıktan sonra artık hedef aralığım belirliydi.
4
Elde olanları sıralayacak olursam, şimdilik sadece bir adet Callbackimiz ve gizli bir sürücümüz bulunuyordu.
Dolayısıyla temizliğin ilk safhasına başlamış bulunuyordum. Bu noktada ilk iş CallBack rutini sistemden kaldırmaktı.
Genel mantık PspCreateProcessCallback dizisinin içerisinden, bu callback adresine sahip fonksiyonu silmektir. Fakat tecrübelerime dayanarak pek güvenli bir yöntem olduğunu söyleyemem çünkü bu derece profesyonelce yazılmış bir virüsün normal olarak bir watchdog timer’ı olmaması garip olur. Dolayısıyla, temizlik esnasında daha jenerik bir yöntem kullanmalıydım ve zararlı herşey yolunda zannederken aslında callbackinin çalışmaması gerekmekteydi. Bunun için PsRegisterProcessCreateNotify rutini analiz etmeye başladım. Kodun içerisinde bulunan dizilere IDA’dan referanslara baktığımda CreateThread apisi dikkatimi çekti. Kodu disasm ettiğimde karşıma çıkan manzara aşağıdaki gibiydi :
5
Bu arada bu tekniği bulmak için epey uğraştımı da itiraf ediyorum, fakat hem güvenilirlik hem de stabilite açısından ekip arkadaşlarım tarafından da epey takdir gördüğünü söylemeliyim. Teknik ne derseniz, register ettiğimiz CallBack rutini siledebilirdim ve bunu programımıza otomatik olarak yaptıran bir kod bloğu da yazabilirdim fakat daha önce de belirttiğim gibi, bunu yapmam rootkit’i kızdırabilirdi (belki de ilerleyen versiyonlarda) ve kedi köpek savaşı başlardı.
Kod parçasına dikkat ederseniz, PspCreateThreadNotifyRoutineCount adında bir Global değişken kontrol ediliyor ve bunun değeri SIFIR ise 805d0483 adresine atlanıyordu. Bu adres ile şu anda bulunduğum adres arasına baktığımda zaten işin en önemli kısımlarının buralarda gerçekleştiğini, yani notification rutinlerin bu alanda çağırıldığını gördüm. Dolayısıyla temizlik esnasında bu değeri sıfırlarsam, rootkit sistemde aktif olduğunu zannedecek fakat aslında OS tarafından çağırılmayacaktı Gülümseme
Önce windbg ile kontrol ettim, sembolün adresine
ed nt!PspCreateThreadNotifyRoutineCount 0, komutunu vererek makineyi çalıştırdım ve hiç bir problem yaşamadım.
Otomasyon programına bunu hemen ekleyip crash vs. ihtimaline karşı tekrar test ettim, canavar gibi çalışıyorduGülümseme
Sonraki aşama enfekte edilen sürücüyü bulmaya gelmişti. Okuduğum analizlerden Storage Stack’da en dipte bulunan SCSI Miniport sürücüsünün enfekte edildiğini biliyordum.

6
En altta benim sistemimde olması gereken vmscsi.sys ya da Windows 7 sistemimde LSI_SAS sürücüleri olması gerekirken 0x82261a38 adresinde “İsimsiz” bir sürücü yerleşmişti. Tespitte kullanılabilecek jenerik bir yöntem daha diyerek yola devam ettim. DriverType değeri de SIFIR, elde var iki Gülümseme
7
DeviceObject’in sürücüsüne baktığımda Major Fonksiyonların tamamının kancalanmış olduğunu gördüm. Belki de DeviceObject komple değiştirilimişti?? Eğer sadece fonksiyonlar kancalandıysa,  bu fonksiyonları nereden bulacaktık?? soruları kafamda dönmeye başladı. Bu noktada direk yazı tahtasına geçtim ve düşünmeye başladım. Tabii bu arada TeamViewer’lar havada uçuşuyor Gülümseme
Bu noktada 3 sorunumuz var :
1. Gerçek DeviceObject’in adresi nerede ?
2. Gerçek DeviceObject’i bulduktan sonra, handlerların adresini değiştirmeye müteakip, TDL’in watchdog thread’i enfeksiyonu restore etmek için bizimle savaşacak. Ne yapmalıyız ?
3. İlk sorunu hallettikten sonra, enfekte edilmiş Driver’ı bulabiliriz fakat NTFS.sys’nin üzerinden geçmeden C:\Windows\System32\drivers\mouclass.sys gibi bir dosya yolunu diskte bulunan Cluster / Sector adresine nasıl çevireceğiz ??? Bu NTFS dosya sisteminin yaptığı şeyleri tamamen kod ile yapıp doğru offsete ulaşmak anlamına geliyordu. Neden mi? Aşağıdaki şekilde DOSYA YOLU diye bir kavramdan anlayan ilk sürücü NTFS.sys’dir. Onun aşağısına indiğinizde DOSYA YOLU kavramı kaybolur ve herşey yerini Sector – Cluster – LCN gibi kavramlara bırakır. Rootkit en alttaki atapi.sys’ye bulaşıyordu ve bu sürücü Diske IN – OUT yapan sürücü olduğu için iş gitgide zorlaşıyordu , yani sözün bittiği yerdeydik Üzgün gülümseme
 
10_8334_1555a83a3be853b
NT Mimarisi ile ilgili undocumented bir çok manevra yapabilirdim fakat, ticari bir üründen söz edildiğinde biraz durmak gerekiyor,  ne kadar undocumented işlem yaparsam BSOD ile karşılaşma ihtimalim o kadar artacaktı o yüzden enine boyuna düşünüp gerekli çizimleri tahtaya yaptıktan sonra kolları sıvadım.
Cevap 1 :
Sanal Makineyi enfeksiyon öncesi haline getirip gerçek device objectin adresini aldım ve sistemi enfekte sisteme döndürdükten sonra bu adresi hafızada aramaya başladım. 0x82261a38 adresindeki VMSCSI device’ının adresi el mahkum hafızada saklanacaktı. 
8
s 80000000 L?7fffe000 82 26 1a 38
komutu ile tüm hafızada gerçek device object’in adresini aradım. Karşıma 100’den fazla entry çıktı. Dolayısıyla bir değerlendirme yapmak için bu kadar hafıza adresini incelemem gerekiyordu.
Analizde en önemli şey TEKRARLAYAN HAREKETLERDEN KAÇINMAKTIR!  Ayrıca bu gibi bir durum mutlaka ileride de işime lazım olacaktı. Bu yüzden PyKD ile küçük bir Python scripti yazmanın uygun olacağını düşündüm ve Visual Studio’da küçük bir script yazdım. Script device object’den driver object’e gidip sürücü geçerli mi değil mi bunu tespit edecekti.
dbgscript
10
Script, 2 tane adresin geçerli olduğunu tespit etti. Bu adreslerden ilkine baktığımda Rootkit tarafından yaratılan DriverObject’in içerisinde saklı olduğunu gördüm. Saklama yöntemine baktığımda hemen TDL 4 ile karşılaştırdım, ikisi de aynı yere saklanmıştı, adresi cebe atarak devam ettim.
Cevap 2 :
Watchdog ile savaşmak gerçekten zor bir iş. Başlangıçta korktuğum şey, rootkit tarafından oluşturulan threadi durdurmak veya sonlandırmak mavi ekrana yol açar mı acaba diye düşündüm fakat korktuğum olmadı. Öncelikle temizlenmesi gereken kancaya bir breakpoint yerleştirdim ve TDL beni yine güldürdü. Aşağıdaki ekran görüntüsüne bakarsanız, zararlı bana olayın farkında olduğunu anlatmaya çalışıyordu.
Açıkça rootkit onu uyuşturmaya çalıştığımın farkındaydıGülümsemeGülümsemeGülümseme
Here comes Johny
Bu çıktıyı veren adresi NOPlamak benim için jenerik bir çözüm sayılmazdı. Ayrıca müşterilerin bilgisayarlarında rahatlıkla mavi ekrana yol açabilirdi. Dolayısıyla başka birşeyler yapmam gerekiyordu. Aklıma ilk gelen yöntem sanal makineyi tek CPU ile çalışacak şekilde ayarlamak ve Irql’yi DISPATCH_LEVEL’e çıkartarak en dipteki sürücüye okuma isteklerini göndermekti. Bu sayede, rootkit’in koruyucu threadinin çalışmasına müsade etmeden gerçek veriyi okuyabildim. Peki birden fazla CPU varsa o zaman ne yapacaktık? Bu durumda da her CPU’ya bir adet DPC gönderdikten sonra tüm CPU’ları kilitleyip üzerinde çalıştığımız CPU’dan okuma işlemini gerçekleştirebilirdik. Bunda da herhangi bir sorun yaşamadım.
Cevap 3 :
İşin en zevkli kısmı bu noktada başlıyordu. Geçtiğimiz ay yazdığım MFT Parser ile diski taramaya başladım. Önce ReadFile apisiyle drivers klasöründe bulunan tüm sys dosyalarının CRC32 hashlerini aldım, sonra da MFT Parser ile aynı klasörü listeleyerek kendi driverımıza bir önceki adımda bahsettiğim adımları icra etmesi için IOCTL’ler göndererek dosyaların DATA Attribute’lerini okudum. Müteakiben aldığım verilerin tekrar CRC32 hashlerini alarak bir önceki ReadFile’dan gelen sonuçlarla, yeni sonuçları karşılaştırdığımda mouclass.sys dosyasının hashlerinin tutmadığını farkettim. Bu şu anlama geliyordu : Rootkit bu dosyayı enfekte etmiş ve sistemi taramaya çalıştığımda bana dosyanın orijinal halini döndürmeye çalışıyordu.
Aşağıdaki resimde program tarafından hesaplanan disk offsetini sürücüye göndermem ile artık NTFS.sys sürücüsünü ve altındaki tüm driverları bypass ederek direk olarak disk erişimi yapabiliyordum.
disk

TDL4 de bir önceki versiyondan farklı olarak BootSector enfeksiyonu gerçekleştiriliyordu. Aslında temel olarak baktığınızda MBR Enfeksiyonunu temizlemek, az önce bahsettiğim sürücüyü temizlemekten çok daha kolay çünkü MBR nin disk offseti belli ve NTFS dosya sistemi ile uğraşmadan direk olarak hedefe gidebiliyorsunuz. TDL4 de zorlayan tek şey, rootkit’in Kernel Debugger DLL’si üzerinde yaptığı değişiklikleri bypass etmek oldu. Cidden çok güzel bir manevra ile bağlantıyı kopartmışlar, fakat elinizde fonksiyon prologlarının yedeği varsa, TDL4 ile de eski versiyonu gibi rahatça savaşabilyorsunuz. Bunun için yapmanız gereken tekşey zararlıyı çalıştırmadan önce .writemem komutu ile apilerin prologlarının yedeklerini almak!
Sonsöz :
Epey uzun bir yazıdan sonra rootkit ile ilgili bir kaç şey söylemek istiyorum.
TDL ailesini incelemek benim için gerçekten çok zevkliydi, şunu rahatlıkla söyleyebilirim : Bu rootkiti her kim yazıyorsa, gerçekten tam bir Kernel KungFu Ustası! Fakat temizleme yöntemi geliştirirken, aklıma gelen birçok “Acaba” var.
En basitinden, watchdog timer’a IO işlemi yaptırsalardı, timerı sonlandırmam veya durdurmam benim için çok tehlikeli olurdu. Ya da çok merak ettiğim, bir sonraki versiyonda Debugger ile Debuggee arasındaki bağlantıyı kopartmak yerine, Kernel Debugger Message Buffer’ı modifiye ederler mi? Bu arada, bu yazıda public ettiğimiz Callback Bypass yöntemini bir sonraki versiyonda görecek miyim ve daha bir çok soru…
Hoşçakalın!



17 comments:

  1. Windbg'a nasıl bukadar hakimsiniz? Windbg için tavsiye edeceğiniz kitap varmı?

    ReplyDelete
  2. @Hakan :
    Teşekkür ederim. Aslında çaresizlikten diyebiliriz:) Windbg'a alışmak gerçekten çok zahmetli bir iş fakat alıştığınızda da hep daha fazlasını öğrenmek istiyorsunuz çünkü IDA Pro ile birleştiğinde yapılamayacak şey yok gibi.

    Kitap konusunda da User mod için düşünüyorsanız "Advanced Windows Debugging" kitabını şiddetle tavsiye ederim, fakat Kernel Mod için çok faydalı olmaz. Bu durumda da Help dosyasındaki komutları sıradan gitmek ve kendi notlarınızı oluşturmak gerekiyor. Ayrıca Analyze-V vb. gibi blogları da Feed Takipçinize eklemeniz güzel trikleri yakalamanıza yardımcı olacaktır.

    ReplyDelete
  3. Ellerinize sağlık Emre, son zamanlarda okuduğum en iyi makale! PspCreateThreadNotify ' de bulduğun trick gerçekten harika :) . Zemana ailesinden teknik yazıların devamını sabırsızlıkla bekliyoruz, yerel güvenlik camiasına katkısı tartışılamaz.

    ReplyDelete
  4. Oldumu şimdi, bizde izleseydik bari :) Sistemden tamamen bağımsız full statick çalışan bir antimalware mi geliyor :))

    ReplyDelete
  5. Ellerinize/Bilgilerinize sağlık. Harikulade bir yazı ve inceleme.

    ReplyDelete
  6. "Kod parçasına dikkat ederseniz, PspCreateThreadNotifyRoutineCount" Thread <-> Process degisikligi olacak sanirim yazim hatasi olmus, yoksa koda baktim goremedim. Bu arada tebrikler guzel bir inceleme olmus.

    ReplyDelete
  7. @Celil :
    Çok teşekkürler Celil, yeni exploitleri bekliyoruz :)

    @OsC :
    Aslında çok düşündüm Video kayıt yapmayı epey uzun bir analiz olduğu için kötü müziklerimle berbat etmeyeyim dedim:):) Bu arada Nokta Atışı yapmışız, doğru tahmin! ama bir kaç özelliği daha olacak dersem yalan olmaz:)

    @Mehmet İnce :
    Teşekkürler Mehmet!

    @x90 :
    Aslında bu yorumu kimin yapacağını merak ediyordum:) Hem Thread hem Process, ikisinde de kullanılan bir global. İki imajdan diğerini eklemişim :(

    ReplyDelete
  8. NotifyRoutineCount yöntemini daha öncede blackhat sunumlarında görmüştüm yani ilk bulan olduğunuzu zannetmiyorum

    ReplyDelete
  9. Çok uzun süredir Rootkitler ve undocumented yapılarla ilgilenmeme rağmen daha önce bu tip bir method görmediğim için referans vermedim fakat yazınızdan sonra da araştırdım, yaptığım
    Google Sorgusu nu epey modifiye etmeme rağmen pek bir şey bulamadım? Hatta notify rutinleri kaldırmak için AntiRootkitlere disasm motoru eklemek zorunda kalınıyor. BlackHat sunumunun linkini verebilir misiniz??

    ReplyDelete
  10. Çok güzel bir inceleme ve makale abi, sadece web sitesi ve muhasebe programı üreten bir ülkede her ne kadar kıymeti bilinmese de.

    Bu arada Türk milletinin yengeç millet olduğunu bir kere daha görmüş olduk. Birbirimizi yükseltmeye uğraşacağımıza yükselmeye çalışanları da ayağından çekmeye çalışıyoruz.

    ReplyDelete
  11. Kitap için çok teşekkürler hocam. IDA Pro ile birleştiğinde derken? Bu arada !chkimg komutuna bayıldım

    ReplyDelete
  12. İsimsiz adsız yorum yapıyorlar, ortaya bişey bırakıp kaçıyorlar.

    ReplyDelete
  13. @Kutalmış :
    Çok teşekkürler Kutalmış :) Yanlışlıkla yazdığını düşünüyorum, adsız olması da sanırım bizim yanlış anlayacağmızdan çekindiği içindir herhalde. Ben de korktum aslında yorumu görünce hata yaptıysam gerekli düzeltmeyi yapayım diye epey araştırdım ama maalesef bulamadım. Çok da büyütülecek bir şey değil, sadece pratik ve etkili bir teknik olduğu için paylaşmıştım. Paylaşmadan gelişmiyor hiç bir şey:)

    @Hakan :
    Ben teşekkür ederim Hakan. IDA Pro ile birleştirme konusu şöyle, WinDBG ile incelediğin kodu extract edebilirsen, IDA ile disasm yaptıktan sonra Edit > Segments > Rebase ile sanal makinedeki BASE'e REBASE ettikten sonra çok güçlü bir analiz ortamına erişebilirsin. Windbg ile incelerken bir nevi IDA ile geleceği görebiliyorsun:):):):) Aksi takdirde, sadece windbgın Assembly penceresini şahsen yeterli görmüyorum. Bu arada bilgi olması açısından, 5.5 sürümünde REBASE özelliğinde bir bug var o yüzden programı açar açmaz sadece bir kere REBASE yapabiliyorsun :( İlerleyen versiyonlarda hata giderilmiş.

    OsC7h :
    :) Aslında ortaya bırakılan şey faydalı olabilirdi fakat pek birşey bulamadım yine de araştırırken bir kaç güzel fikir daha buldum:) Yani işe yaradı:)

    ReplyDelete
  14. Merhabalar öncelikle yazı için teşekkürler elinize sağlık Emre Bey. Cevaplamanızı isteyeceğim basit bir kaç sorum olacak.

    1- LDT ve GDT check yaparken bunun icin hali hazırda bir script kullanıyormusunuz eğer öyleyse paylaşmanız mümkün müdür ? manuel check yapıyorsanız bunuda paylaşmanızı isteyeceğim. ve hangisi sağlıklı olanıdır ?

    2- eğer ed nt!PspCreateThreadNotifyRoutineCount 0 yerine ed nt!PspCreateProcessNotifyRoutineCount 0 yazmış olsaydık buradaki tepkime ne olurdu?

    3- yazı içerisinde varsa göremedim özür dilerim eğer yoksa bu zararlı yazılımı uygulama yaparak birşeyler öğrenebilmemiz adına paylaşmanız mümkünmüdür ?

    şimdiden teşekkür ederim.
    sevgiler.

    ReplyDelete
  15. Merhaba,

    Geç cevabım için özür dilerim, LDT ve GDT'yi incelemek için Laboskopia Scriptlerini kullanabilirsiniz.
    Manuel check yapmak da zor değil açıkçası. Bu iki tablo hakkında en detaylı bilgiyi de Protected Mode Software Acrhitecture'dan edinebilirsiniz. Hatta bu günlerde bir iki zararlıda bu yöntemin kullanıldığına dikkat ettim, sisteme normal dışı yollardan girmek için bir Call Gate install ediliyor. Bununla ilgili de vakit bulabilirsem bir makale yayınlayacağım.

    ed nt!PspCreateThreadNotifyRoutineCount 0 yerine ed nt!PspCreateProcessNotifyRoutineCount 0 yaparsak henüz denemedim, yarın testi yapıp buradan paylaşırım. Aralarında ANCAK VE ANCAK tarzı bir bağlantı olabilir, yani birisi aktifse diğeri de aktif olmalı vs. gibi, inceleyeceğim en kısa zamanda, ben de merak ettim.

    Testte kullandığım zararlıyı göndermem için emre.tinaztepe@zemana.com'a mail atarsanız, rarlayıp gönderebilirim.

    Teşekkürler,

    ReplyDelete
  16. Merhaba,

    ThreadNotify ile ProcessNotify değişkenlerinin ikisi de CreateThread içerisinde kullanılıyor. Bunun maksadı da şu : Thread yaratılırken eğer aktif thread sayısı SIFIR ise, bu yeni bir process yaratıldığı anlamına geliyor dolayısıyla, yeni yaratılan bir bir processin ilk threadi olduğu için notification mekanizmasını engellemiş oluyoruz:)

    Sözün özü, iki değişken de PspCreateThread içerisinde kullanılıyor ve eğer oluşturulan thread, söz konusu process'in ilk threadi ise ProcessNotification gönderiliyor. Mantığı aynen değüişken isimlerind olduğu gibi, Process'i ED lerseniz process notificationları öldürüyorsunuz, Thread'i ED lerseniz, thread notification ları öldürüyorsunuz.

    ReplyDelete
  17. Yazdıklarınızı okudum. Hiç bilgim olmadığı için, konunun özü dışında, hiç birisini anlamadım.

    Bu başarınızı kutlar, alanınızda çığır açacak birçok çözüm bulmanızı dilerim.

    CB

    ReplyDelete