C#nedir?com
 
Site İçi AramaSİTE İÇİ ARAMA
Üye Girişini AçÜye GİRİŞİ
Üye girişi için tıklayın.
Kullanıcı Adı
Şifre
 
Beni her zaman hatırla
Bir hafta boyunca kullanıcı bilgilerinizi kullanıcı çıkışı yapana kadar hatırlar. (Paylaşılan bilgisayarlarda önerilmez.)
 
Şifremi / Kullanıcı Adımı unuttum.
 
.net TV RSS GÜNCEL MAKALELER
emre TAŞ
Soyutlaştırma
emre TAŞ
yazının devamı >
Hüseyin Çelik
Captcha ve Captcha'nın OCR ile analizi
Hüseyin Çelik
yazının devamı >
Önsel Akın
NHibernate
Önsel Akın
yazının devamı >
Mehmet Sencer Karadayı
Lightswitch' e Giriş
Mehmet Sencer Karadayı
yazının devamı >
emre TAŞ
C' de Makrolar
emre TAŞ
yazının devamı >
Makale Gönder Bende Yazmak İstiyorum
.net TV RSS.NET TV
Android OS Üzerinde Yazılım Geliştirme Android OS Üzerinde Yazılım Geliştirme - Kubilay Kulaoğlu
Android ile Programlamaya Başlangıç Android ile Programlamaya Başlangıç - kubilay Kulaoğlu
WPF-Chart  Kontrolü WPF-Chart Kontrolü - Görkem Sezgin
Silverlight'a Temel Bakış Silverlight'a Temel Bakış - Görkem Sezgin
Linq Teknolojisi Linq Teknolojisi - Görkem Sezgin
Video Gönder Bende Yayınlamak İstiyorum
anketAnket
.NET Yeniliklerini Takip Etmek Sizin İçin Nekadar Önemli ?







Diğer anketler ve sonuçları için tıklayın.
  Diğer Herşey
Sponsorlar
BT Akademi
Medya Portakal
Video Hosting Sponsoru
Csharpnedir.com bir Ineta üyesidir
Uzman Abi
Her Yönüyle C# - Sefer Algan
 
Yeni bir decompile aracı : FOX
C#nedir?com üyelerinden ve forum moderatörlerinden Ozcan Değirmenci tarafından geliştirilen ve Amerikan firması Xenocode'da satılan Fox Kod Analiz aracının final sürümü çıktı. C#nedir?com forumlarında tartışılmaya başlanan bu fikir artık dünya çapında en çok kullanılan decompiler aracı olma yolunda emin adımlarla ilerliyor. Özcan Değirmenci Fox'un serüvenini sizler için kaleme aldı...
Özcan Değirmenci, C#nedir?com üyelerinin özellikle de C#nedir?com forumlarını takip eden üyelerimizin yakından tanıdığı isim. Bundan yaklaşık 1.5 yıl önce forumlarda derlenmiş .NET uygulamalarının IL’’den C# yada başka bir .NET diline rahatlıkla dönüştürülebileceği üzerine tartışmalar geçiyordu. Bu tartışmalar kimini memnun derken kimini mutsuz ediyordu. Her fırsatta .NET platformunun IL kodunu gizleme gibi bir amacı olmadığını vurgulayan Özcan Değirmenci decompile işleminin rahatlıkla yapılabileceğini düşünüyordu. Forumda decompile işlemleri üzerine derin tartışmalar yaşanırken Özcan Değirmenci daha sonra dünyadaki sayılı decompiler’’ların arasına girecek olan aracını kodlamaya başlamıştı bile. Aradan bir iki ay geçti ve fikri C#nedir?com forumlarında çıkan ve Fox adını koyduğu decompiler aracının ilk sürümünü yine C#nedir?com forumlarında duyurdu. Bir çok üyemiz bu projede testçi olarak yer aldı ve Fox’’un gelişimine katkıda bulundu. Nihayet Fox’’un çalışabilen versiyonu çıktığında bir çok kişi tarafından denendi ve beğenildi. Beğenenlerden biriside merkezi Seattle’’da (USA) bulunan kod koruma ve decompile konusunda uzman bir firma olan Xenocode oldu. Böylece Fox’’un bu gelişimi C#nedir?com forumlarından çıkarak Xenocode firmasının ticari bir uygulamasına dönüştü. Evet, Fox tüm hakları ile Özcan Değirmenci tarafından Xenocode firmasına satıldı.

Fox Decompiler aracının 14 günlük deneme sürümünü http://www.xenocode.com/Products/Fox/ adresinden indirebilirsiniz.

Fox aracının C#nedir?com’’daki gelişim evresini merak ediyorsanız aşağıdaki bağlantılarda geçen bazı tartışmaları okuyabilirsiniz.

Decompiler mevzusunun tartışıldığı mesajları

Fox ile ilgili tartışmalar

Yine Fox hakkında mesajlar

....

Ayrıca http://www.csharpnedir.com/forum2/search_form.asp adresindeki arama kutucuğuna "fox" yada "decompiler" kelimelerini yazıp aratırsanız daha fazla içeriğe erişebilirsiniz.


Şimdi binlerce kişi tarafından trial versiyonun indirildiğini belirten Özcan Değirmenci gelen tepkilerden oldukça memnun olduğunu belirtiyor. Değirmenci, son versiyonu geçtiğimiz günlerde yayınlanan Fox’’un serüvenini ve gelişim aşamalarını birazda teknik detaylarını C#nedir?com için kaleme aldı. Bundan sonrasını kendisinden dinleyelim :

"Bundan 1,5 sene öncesinde www.csharpnedir.com forumlarında ilk defa tartıştığımız ve duyurusunu yaptığımız Fox o tarihte yazılmaya başlandı. İlk 5 ay kadar çok vaktim olmadığı için bakamadım. Ama daha sonraki 4 ay süresinde bu konuya daha fazla vakit ayırıp Fox’un ilk betasını çıkarttım. Fox o zamanlar cok daha işin başında olduğu için decompile ederken bir çok hatalar alıyorduk. Çünkü decompile mantığında metadata ları okumak işin en basit kısmıdır. MetaDatalar okunduktan sonra o metadatal’’ arı ayıklayıp(parse) anlamlı tablolarda ortaya çıkardıktan sonra bir methodun gövdesini dosya içinden okuyup daha sonra bu gövdeyi anlaşılır kod haline çevirmek en zor kısmıdır."

Özcan Değirmenci

Fox’’un Serüveni

Bu makalede sizlere kısaca Fox’un gelişme sürecini, bir decompiler nasıl yazılırı ve decompilerlardan nasıl korunuluru anlatmaya çalışacağım. Konuyu elimden geldiği kadar kısaca anlatmaya çalıştım ancak yinede uzun bir makale olduğu için hepinizden özür dilerim… Microsoft’un en yeni ve en gözde yazılım geliştirme platformu olan .NET ilk çıktığında benimde içine dahil olduğum bir çok insan bu platformdaki açıklara dikkat bile etmeden platformun onlara sağladığı geniş imkanlara ve program yazmadaki rahatlıklarına kapılıp bu platforma geciş yaptı. Aslında JAVA programcıları için çok fazla ses getirdiği söylenmesede özellikle Visual Basic yada ASP kullanan yazılım geliştiricileri açısından .NET gerçekten büyük bir devrimdi. Aslında coğu programcı sadece sözlere kanıp .NET ile uğraşmaya başladı bile diyebiliriz. Ortada herşeyin çok daha kolay yapıldığına dair sözler dolaşıyordu ama yinede kimse olayı çokta bilmiyordu. Ancak zaman geçtikçe görüldüki Microsoft cidden doğruyu söylemişti. Platform gerçekten çok esnek davranıyordu. Ve yine beklenmedik şekilde çok az bug ile ve en azından Microsoft platformlarının hepsi ile son derece uyumlu çalışıyordu. Bütün bunların yanında birde dünyanın belkide o zaman kadarki en sağlam editörüde .NET geliştiricilerinden yana olunca ortaya tam bir şaheser çıktı diye düşünüyorduk. Ne ilk çalışmadaki bekleme nede editörün aşırı hafıza kullanımı bizim açımızdan bir sorun değildi. O zamanlar bende .NET’i ilk duyduğumda merak etmiş ve direk onla ilgilenmeye başlamıştım. Ancak daha sonra zaman geçtikçe bu platformdaki açıklar üzerine düşünmeye başladım. Aslında kendi kendime düşünmekten ziyade bir arkadaşın tavsiyesi uzerine indirdiğim ‘Anakrino’ programı ile şoke olmuştum. Programla kendi yazdığım exe nin kodlarını hemen hemen ayni şekilde görüntüleyebiliyordum. Decompiler sözcüğü ile ilk defa o zamanlar haşır neşir oldum. Bu sözcük beni yeni program yazmaya başlamış biri olarak çok etkilemişti. Açıkcası karizmatik geldi bile denilebilir.

O günden sonra bu konu uzerine biraz daha ilgilenir oldum. Daha sonrasında yine bir arkadaşım sayesinde Reflector’la tanıştım. ‘Reflector’ ‘Anakrino’ya göre daha iyi decompile eden bir araçtı. Hatta daha sonra Lutz Roeder’den öğrendiğime göre Anakarino ile Reflector aslında aynı amaçla beraber başlamışlar ancak daha sonra aralarındaki anlaşmazlıktan dolayı Saurik ayrılmış ve kendi bir tool çıkarmış ve Reflector ayrı bir tool olarak çıkmış. Bu her iki decompiler’’ın yapabildikleri gerçekten çok ilginç gelmişti bana.

Aradan geçen zamanlar esnasında decompiler’’lar bir çok insan tarafından hala yeni yeni duyulmaya başlıyordu ki bu nedenle www.csharpnedir.com’da bu konuda bir çok soru çıkmaya başlamıştı. Bu sorularda daha çok bunların ne kadar rahatsız edici olduğu üzerine tartışıyorduk. Ama benim için hayatta en sevmediğim şey belkide başkalarının yaptıkları üzerine konuşmak olduğu için o zamanlar bende bir tane decompiler aracı yazmaya karar verdim. İşte yaklaşık bundan 1,5 sene öncesinde www.csharpnedir.com forumlarında ilk defa tartıştığımız ve duyurusunu yaptığımız Fox o tarihte yazılmaya başlandı. İlk 5 ay kadar çok vaktim olmadığı için bakamadım. Ama daha sonraki 4 ay süresinde bu konuya daha fazla vakit ayırıp Fox’un ilk betasını çıkarttım. Fox o zamanlar cok daha işin başında olduğu için decompile ederken bir çok hatalar alıyorduk. Çünkü decompile mantığında metadata ları okumak işin en basit kısmıdır. MetaDatalar okunduktan sonra o meta dataları parse edip anlamlı tablolarda ortaya çıktıktan sonra bir methodun gövdesini dosya içinden okuyup daha sonra bu gövdeyi anlaşılır kod haline çevirmek en zor kısmıdır. Bu esnada gövde okunup IL ifadeleri ortaya çıktıktan sonra bir compiler’’in bu kodları nasıl oluşturabileceğini düşünüp buna göre ters işlem yapıp ortaya anlaşılır kod çıkartmak gerekir. İşte işin en zor kısmı olan bu kısım surekli gelişmesi gereken bir kısımdır ki yeni çıkmış bir decompiler’’da bu tip hatalardan kaynaklı hatalar oluşması çok normaldir. Ancak isimlerini buraya yazamayacağım kadar çok arkadaş o zamanlar www.cshaprnedir.com’daki yazılarımı görmezden gelmeyip bana testler konusunda destek çıktılar. Bu esnada Fox biraz daha gelişti ve son beta 1.0.8 versiyonu ortaya çıktı.

Bu noktada benim için aslında olayın çok daha ileri gitmesi için bir sebep yok görünüyordu. Çünkü ben istediğim şeyi gösterip bunu bir Türk’ün de isterse yapabileceğini ispatlamıştım. Hatta bir ara Fox’un Open Source olup olmaması konusunda yaptığım anket nedeni ile Reflector’un yazarı ‘Lutz Roeder’ den bile tepki alıcak kadar ismi duyulmuştu o zamanlar. Ayrıca istediğimizde neler yapabileceğimizin de bir göstergesi olmuştu. Programcılık yaparken tek ihtiyacımız olan şey cesaret edip başlamaktir. Ben Fox’u yazacağım dediğimde hiç unutmam bir arkadaşım bana “Bence bunu siteden duyurma çünkü ne yapacağını bile bilmeden duyurup da yapamazsan insanlara karşı sorumlu olursun” demişti. Belki kendi açısından haklı idi ama benim kendi düşünceme göre bir işi yapmayı istemek ve başlamak onu yapmakla hemen hemen ayni şeydir. Gerisi sadece zaman meselesidir. Nitekim öylede oldu; işe başlarken meta datalar hakkında çok bilgisi olmayan, CodeDom u doğru dürüst sınıflandırmayı bile bilmeyen, IL den çok çok az anlayan biri olarak elimdeki 4 Microsoft dökümanı sayesinde bütün bunlar zamanla çözüldü ve ortaya bir decompiler aracı çıktı. Beta sürümünden sonra çıkan başka terslikler ve engeller nedeni ile Fox o beta asamaşında uzun süre kalmak zorunda kaldı. Ancak bir süre sonra Fox konusunda “XenoCode” benle gürüşmek istedi ve Fox’u tüm hakları ile satın almak istediklerini belirtti. Yapılan görüşmeler sonucunda Fox’u satın aldılar ve bende onlar icin Fox’u geliştirmeye devam ettim. Fox o günden sonra ‘Xenocode Fox’ oldu. Bügün ise Fox şu anda en iyi .NET decompiler olarak lanse edilen ve 2 haftada 20.000 download!a ulaşan bir program oldu. Bu beklenmedik başarı bügün Xenocode’u ve beni Fox’u daha da geliştirmeye itiyor.



Şu anda bir çok decompiler’’ın çözemediği yada Label’larla çözdüğü kodları bile daha anlamlı kodlar haline çevirebilen Fox bunun yanında “Export Visual Studio .NET 2005” özelliği ile de ilgi çekiyor. Bu özellik sayesinde Fox verilen bir exe yada dll’i sanki siz onu proje yapıp da sonra build etmişsiniz gibi geriye dönüşük Proje dosyasını oluşturuyor. Bu esnada Component, Form, UserControl gibi designer destekli class’ları anlıyor varsa bunların resx dosyalarını onlarla ilişkilendiriyor ve hatta AssemblyInfo.cs, csproj ve sln dosyalarını bile otomatik üretebiliyor. Oluşan bu projeye VS.NET ile açıp tekrar buil edebilirsiniz hemde hiç kod değiştirmenize gerek kalmadan. .NET 2.0 la gelen Generics’’lere tam desteği var. Her türlü DllImport yada Marshaled call ifadelerini tam manası ile çözüyor. Normal attribute çözümüni yapabildiği gibi; return: ve param: attribitute türlerinide çözebiliyor. Ayrıca sınıfların Base ve Derived tiplerini gösterebiliyor. Text Search yapabiliyor. .resources dosyalarını tam manası ile çözüp resx leri oluşturabiliyor. System.Resources.ResourceReader 1.0 ve 2.0 versiyonuna tam desteği var. Yani bu ResourceReader ların hangisi için hazırlanmış .resources dosyası olursa olsun onu tam bir resx şeklinde çözebiliyor. Bilinen resx data tiplerinin yanında application/x-microsoft.net.object.binary.base64 ve application/x-microsoft.net.object.bytearray.base64 her ikisinede desteği mevcut. Unicode textleri otomatik algılayıp ona göre yazabiliyor. Ayrıca hyperlinked görüntü sayesinde istediğiniz tip yda üye elemana tek tıkla ulaşabiliyorsunuz. C#, VB.NET ve IL dillerinde dönüşüm yapabiliyor. Yine C# ve VB.NET dillerinin her ikisindede Export edebilme özelliği mevcut. Ayrıca Tab Page görünümü sayesinde aynı kodu iki dilde açıp karşılaştırma yapabilirsiniz. Ayrıca Fox da oto casting vardır. Yani decompile edilen kodda method call lar kontrol edilir ve eğer tipler uyumsuzsa otomatik casting kodları ekler. Bunun gibi daha bir çok decompile stratejisini yapısında barındırır.



Fox’un aslında biz Türk programcılar için en önemli özelliği ise bizimde istediğimizde bir şeyler yapabildiğimizdir ve sonuçta yaptığımız şey dünya standartlarında ses getirecek cinsten bile olabilir. Zaten Fox’u yazarken bunu göstermeyi düsünüyordum ve sanıyorum bugün bunu az çok ispatlamış bulunuyoruz. Dünyanın üzerine en cok yatırım yapılan platformlarından birinde yazdığınız tüm kodları sanki build edilmemiş gibi çözen bir ürün.

Fox ve benzeri programlar çoğu yazılımcı tarafından eleştirilselerde emin olduğum bir şey varki bu eleştirenlerin hemen hemen hepsi bu araçları kullandığıdır. Bu araçlar eleştiriliyor çünkü; onların gözünde başkasının kodunu çalmaya yarayan araçlar. Fox onların günlerce olan emeğine hiç aldırmaksızın tüm kodu çözmek için yaklaşık bir kaç dakika harcıyor ve onların yaklaşık 1 yıllık emeğini çalıyor. Aslında olay benim gözümde bu şekilde canlanmıyor. Benim gözümde ise Fox ve benzeri decompilerlar birer kurtarıcı. Bir windows programcısı olarak Fox hem benim yapabileceklerimi sürekli geliştiriyor hemde bir çok kez beni günlerce uğraşacağım bir şeyi cok daha kısa sürede yapmama imkan sağlıyor. Örneğin bir windows kontrolünde yapmaya calıştığım bir şeyin neden olmadığı konusunda takıldığımda Fox’la System.Windows.Forms.dll’ini çözüp o control’ün kodlarına bakıp bu hatanın nereden kaynakladığını çok rahat bulabiliyorum. Yada örneğin AxWebBroser tarzında basit bir browser objesi yazmak için shdocvw.dll’i açıp içinden gerekli COM interfacelerine bakıp benzerlerini yazıp kendim AxHost dan bir control türeterek istediğim işlevi yapacak bir Web Browser türetebiliyorum. Bunlar işin cok basit kısımları. Bence bundan da önemli olan şey Fox’dan öncede decompiler araçları vardı. Yani Fox’un olmaması durumu değiştirmez. Birde kötü niyetli kullanımlar bizi bağlamaz. Eğer biz herşeyin kötü niyetli kullanılacağını düşünüp ona göre davranmaya kalkarsak sonuçta üretim yapmakdan çıkar adam akıllı işler yapamayız. Bu tip araçları kullanmaya başladığınızda sizlerde işinize ne kadar yaradıklarının farkına varıcaksınız ve cidden böyle bir araca sahip olduğunuz için sevineceksiniz. En azından ben kendi adıma Fox yada benzeri araçlarsız program yazmamın daha zor olucağına inanıyorum.



Şimdi yukardaki uzun Fox hikayesinden çıkıp isterseniz bu decompile olayında biraz daha işin içine girip bazı ip uçları vererek bu işi nasıl yaptığımızı detaylandıralım. Tabii burada çok kısa değinebilmemiz ancak mümkün. Detayları atlamak şartı ile olayın kısa mantığını anlatmak bile yeteri kadar uzun olucaktır. 100.000 satır kodu olan bir programın yaptığını burada detaylı anlatmak da çok mantıklı olmasa gerek.

Biraz da teknik detay ...

.NET Dosyaları birer PE(Portable Executable) dosyalarıdır. Bu dosyalar ilk iki byte’ında 0x5a4d “MZ” değerini taşımak zorundadırlar. Daha sonra 0x3c offsetindeki 4 byte ise bize bu dosyanın PE flagının yerini belirtir. Buradan okunan 4 byte’ın bize verdiği integer degerdeki offset e gidip 4 byte okuduğumuzda ‘PE\0\0’ yani 0x00004550 değerinin olması gerekiyor. Bu kontroller yapıldıktan sonra dosyanın PE dosyası olup olmadığı anlaşılır. Daha sonra ise bu dosyadan COFF (Common Object File Format) Headerı okunur. Bu header bize bu dll in hangi tipde makinalarda calışması için compile edildiği, bazı COFF flaglarını ve OptionalHeaderSize gibi bazı gerekli bilgilerini verir. Bu optional header size sayesinde dosyanın DataSectionlarının offsetlerini bulabiliriz. COFF Header dan hemen sonrasında ise OptionalHeaderlar gelir. Bunları üçe ayırabiliriz. StandartFields, NTSpecificFields ve DataDirectories. StandartFields bize dosyanın exe yada dll olduğunu belirten BaseOfCode (0x00400000 exe, 0x10000000 dll), exe ise EntryPoint inin RVA (Relative Virtual Address) ini, CodeSize ını (genelde text section’ın boyutu, daha dogrusu tum code sectionlarının toplam boyutu) gibi bazı önemli fieldlar barındırır. NTSpecificFields ImageBase(dll, exe, CE_Exe), Section bilgileri, SubSystem (WındowsGui, WindowsCui, Native etc.etc.), DllCharacteristic gibi bazi bilgileri barındırır. DataDirectories ise; Export Table, Import Table, Resource Table, Exception Table, Certificate Table gibi Image Data Directorylerin boyutu ve RVA larını barındırır.

Daha sonra DataSection RVA sına gidip buradan SectionHeaderlarını okuyabiliriz. Bir .NET çıktısında COFFHeader da belirtilen NumberOfSections kadar section header vardır. Her section header o section’ın RVAsını, RawDataSize gibi bilgileri barındırır. Bu headerları kullanarak aradığımız bir RVAnın bu dosyada hangi section’a denk düştüğünü bulup ona göre bu section’ın RVA sı + bizim aradığımız RVA şeklinde aradığımız yere ulaşabiliriz.

Daha sonrasında bu dosyanın CLIHeaderını DataDirectoryler listesindeki CLIHeader ın RVA sına giderek okuyabiliriz. CLIHeader Meta Data, Resources, StrongNameSignature, CodeManagerTable, VtableFixups, ExportAddressTableJumps, ManagedNativeHeader gibi ImageDataDirectoryleri ve bazı flagları barındırır.

CLIHeader dan sonra MetaData ları okuyabilmek için MetaDataHeader’ı okumamız gerekir. Bunun için CLIHeader’ın Meta Data ImageDirectory sinin RVA sına gidip okuma işlemini gerçekleştirmemiz gerekir. Meta Data header bazı flagların yanında en önemli olarak bize bu dosyanın Heaplerinin bilgilerini verir. Bir .NET dosyasında 5 tane heap header vardır. Bunlar; StringsHeap(#Strings), BlobHeap(#Blobs), GUIDHeap(#Guids), USHeap(#US) ve Tables Heap(#-, #~ ) dir. Sırası ile açıklamak gerekirse;

StringHeap: dosyadaki stringlerin tutulduğu yerdir. Örneğin method isimleri, method reference isimleri, field reference isimleri, field isimleri hep burada tutulurlar.

BlobHeap:
Bloblar komplike data yapılarıdır. Burada bir byte[] şeklinde bulunan signaturelar çözülerek kullanıldıkları yere göre değişik anlamlar içeren veriler barındırırlar. Örneğin bir method’a ait referans için o method’un tokeni yada signature’ı gibi. .NET 2.0 la birlikte gelen Genericsler sayesinde bu heap’in önemi daha da arttı ve artık burada TypeInstanceların ve Method Instantiations’lar da tutulmaya başlandı ki bunlar bir generic type dan yeni bir type instance yarattığınızda veya bir method’un generic çağrılmasında o instance’ın çözülebilmesi için gerekli type signature’ları barındırır.

GUIDHeap:
Programda kullandığınız GUID’leri barındırır.

USHeap:
User String’leri yani kullanıcı tanımlı mesaj yada benzeri stringleri içinde barındırır.

Tables Heap:
MetaData Tablelarını barındırır.

Şimdi sıra MetaData Table’’larının kaçar elaman barındırdığını okumaya geldi. Bunu okumak icin #~ yani TablesHeap header’da belirtilen offset e gideriz ve orada sırası ile tüm table’ların kaç tane eleman barındırdığını okuruz. Daha sonra sting heap’e gider ve oradan tüm stringleri okuruz. String heap de stringler ‘\0’ yani Null Terminated string array şeklinde barındırılır. Daha sonra blob heap’e gider ve blobları okuruz. Bloblar sırası ile dizilmiş şekildedirler. Her blob’un ilk başında onun encode edilmiş şekilde kaç byte’dan oluştuğu vardır. Bu byte sayısı kadar byte[] ise onun datasını barındırır. Sonrada Guid heap’e gider ve oradan 16 şar byte lık arrayler şeklinde guidleri okuruz. Daha sonra US Heap’e gideriz ve yine blob da olduğu gibi peşpeşe gelen row lar şeklinde her row un başında encode edilmiş int şeklinde kaç byte olduğunu okur sonrada o kadar byte[] şeklinde string’i okuruz.

Burada kısaca .NET in data tutma şeklinede değinmek gerekirse. .NET öncelikle çok sık kullanılan data tutma şekillerinden biri encode edilmiş inttir. Encode edilmiş int dediğimiz yapı ile int değerinin en az byte kullanılarak tutulması amaçlanmıştır. Bunu şu şekilde çözümleyebiliriz.

int DecodeInt32(BinaryReader reader)
{
           int value = reader.ReadByte();

           if ((value & 0x80) == 0)
                      return value;

           if (value == 0xff)
                      return -1;

           if ((value & 0xc0) == 0x80)
                      return ((value & 0x3f) << 8) | reader.ReadByte());

           return ((value & 0x3f) << 24) | (reader.ReadByte() << 16)
                                            | (reader.ReadByte() << 8) | reader.ReadByte();
}

.NET dosyalarında bir GUID, Blob yada String Index’’i ise 2 yada 4 bytelık bir sayısal değer olarak tutulur. Bunu bulmak için MetaData Header da okuduğumuz HeapSizes değerini kullanabiliriz.

int stringLength = (int) (((HeapSizes & 0x01) != 0) ? 4 : 2);
int guidLength = (int) (((HeapSizes & 0x02) != 0) ? 4 : 2);
int blobLength = (int) (((HeapSizes & 0x04) != 0) ? 4 : 2);

Buna göre eğer .NET dosyasında herhangi bir yerde örneğin Blob okumamız gerektiğinde bu dosyanın blupLength’’ine bakarız ve eğer 2 ise 2 byte okuruz aksi takdirde 4 byte okuruz. Bu sayede .NET dosyasının boyutu küçültülmüş oluyor.

Yine aynı şey Meta Data Table’’larında da geçerlidir.

MetaData Table Header da bulunan 64 int elemanlı Rows dizisi bize bu dosyanın MetaDataTable’’larının her birindeki eleman sayılarını veriyordu. Buradan hareketle hangi Table’’da kaç eleman olduğuna bakarız ve eğer eleman sayısı 0xffff den küçükse bu durumda 2 bytelık bir değer büyükse 4 byte lık bir değer okuruz.

Örneklemek gerekirse eğer dosya içinde bir TypeRef okumamız gerektiğinde TypeRef table’’ında yani 1. index deki Row countuna bakarız eger bu sayı 0xffff den kuçukse bu durumda 2 byte lık bir değer okuruz aksi halde 4 bytelık bir değer okuruz. Okuduğumuz bu değer o table dan bir index i belirtir.

Şimdi kaldığımız yerden devam edicek olursak. Heap okumasını tamamladığımıza göre şimdi sıra en önemli yerlerden biri olan MetaData Tablelarını okumaya geldi. .NET’’te tanımlı 44 tane MetaDataTable vardır. Bunları yazmak ve önemlilerini kısaca yazmak gerekirse;

Module = 0:

Module leri barındırır. Bir dll yada exe içinde birden fazla module barındırabilir. Genelde bu sayı 1 dir. Yani her .NET exe sinde ve dll inde bir tane modül vardır. Module Table ındaki her bir row için sırası ile: Generation, Name, MvId, EncId, EncBaseId olmak üzere 5 tane colon vardır.

TypeRef = 1:

Bu assembly haricinde başka bir assemblyden çağrılan ve kullanılan tüm Type Reference lar için birer row girilir. Örneğin siz int a diye bir değişken tanımladığınızda burada otomatikman int için bir row oluşturulur. Çünkü int bir başka assembly de (mscorlib.dll) tanımlıdır ve oradan kullanılır. TypeRef tableında her bir row için: ResolutionScope, Name ve Namespace olmak üzere üç kolon mevcuttur.

TypeDef = 2 :

Bu assembly içinde tanımladığınız tüm typelardır. Yani class, delegate, struct ve enumlar burada birer row seklinde tutulurlar. Her bir TypeDef için sırası ile Flags, Name, Namespace, Extends, FieldList ve MethodList olmak üzere 6 tane colon bulunur.

FieldPointer = 3 :
Field = 4 :

Bu assembly içindeki tüm TypeDeflerin herbirinin içindeki tüm fieldlar bu table da bulunur. Yani yarattığınız tum class, struct ve enumlar içindeki fieldlar buraya konulur. TypeDef deki type tanımlamalarındaki FieldList bu table da bir index E karşılık gelir. Yani o typedef in fieldlarının bu table da hangi index den başladığını bu sayede bulabilir ve fieldları buradan okuyabilirsiniz. Field table ında her field için sırası ile; Flags, Name ve Signature olmak üzere 3 colon vardır.

MethodPointer = 5:
MethodDef = 6:

Bu assembly içindeki yarattığınız tüm TypeDeflerin herbirinin içindeki tüm methodlar bu table da bulunur. Yani yarattığınız tüm class, delegate ve struct ların methodları buraya konulur. TypeDef deki type tanımlamalarınındaki MethodList bu table da bir indexe karşılık gelir. Yani o typedef in methodlarının bu table da hangi index den başladığını bu sayede bulabilir ve methodları buradan okuyabilirisiniz. MethodDef table’ında her bir methoddef için sırası ile; RVA, ImplFlags, Flags, Name, Signature ve ParamList olmak üzere 6 colon vardır.

ParamPointer = 7 :
Param = 8 :

Bu assembly içinde yarattığınız tüm Type Def lerde yazdığınız tüm Methodların kullandığı parametreler buraya konulur. Yani MethodDef table ındaki her method un varsa parametreleri buraya konulur. MethodDef deki her row un ParamList i bu tableda bir index’e denk gelir. Böylece o methodun parametreleri buradan okuyabilirsiniz. Param table’ında her parametre için sırası ile; Flags, Sequence ve Name olmak üzere üç kolon vardır.

InterfaceImpl = 9 :

Bir TypeDef in hangi interface leri implement ettiğini bulmak için kullanılır. Yani her eğer bir Type Def bir interface i implement ediyorsa; Örneğin class MyClass : IDisposable gibi; bu durumda bir row bu table da oluşur. Bu tabeldaki her bir row da sırası ile Class ve Interface olmak üzere iki kolon vardır. Bu kolonlardan ilki hangi TypeDef için bu row un olduğunu belirtir ki bizim örneğimizde bu MyClass ve ikincisi ise TypeDef yada TypeRef tableında ki bir row a karşılık gelerek hangi interface olduğunu bulmamızı sağlar. Tabi bizim örneğimizde IDisposable.

MemberRef = 0xA :

Herhangi bir TypeDef, TypeRef, ModuleRef, MethodDef veya TypeSpec içindeki bir member’a referas sağlamak adına kullanılır diyebiliriz. Burada her bir member için oluşan row da sırası ile Class, Name ve Signature olmak üzere 3 kolon barındırır. Burada Class hangi table la alakalı olduğunu bildirir.

Constant = 0xB :

Tanımladığınız constantlar burada birer row şeklinde belirtilirler. Burad aher bir row Type, Parent ve Value olmak üzere 3 kolon vardır. Burada Type burada tutulan değerin Native Element Tipini belirtir. Parent ise Field, Parametre yada Property table ında bir index belirtir. Value ise bir blob indexini belirtir.

CustomAttribute = 0xC :

Tanımladığınız tüm CustomAttributelar burada bulunur. Her custom attribute için Parent, Type ve Value olmak üzere 3 colon vardır. Parent MethodDef, Field, TypeRef, TypeDef, Param, InterfaceImpl, MemberRef, Module, DeclSecurity, Property, Event StandAloneSig, ModuleRef, TypeSpec, Assembly, AssemblyRef, File, ExportedType ve ManifestResource tablelarından birinde bir index belirtir. Type ise TypeDef, TypeRef, MethodDef, MemberRef ve UserStrin den herhangi birinde bir index belirtir. Value ise blob şeklinde Value yu belirtir.

FieldMarshal = 0xD,
DeclSecurity = 0xE,
ClassLayout = 0xF,
FieldLayout = 0x10,
StandAloneSig = 0x11,
EventMap = 0x12 :


EventMap bir relation table’ıdır diyebiliriz. Bu table da Event’ler ve bunların hangi TypeDef e ait oldukları ilişkilendirilmiştir. Her bir EventMap row unda sırası ile Parent ve EventList olmak üzere iki colon vardır. Parent hangi TypeDef in eventleri olduğunu EventList de event tableında bir index belirtir.

EventPointer = 0x13,
Event = 0x14 :


Assembly içindeki tüm TypeDef lerinizde tanımladığınız tüm eventler burada birer row şeklinde girilir. Her bir event row unda sırası ile EventFlags, Name ve EventType olmak üzere 3 colon vardır. EventFlag flagları, Name ismini ve EventType da TypeDef yada TypeRef içindeki bir row da o eventin Handler tipini belirtir.

PropertyMap = 0x15 :

EventMap in benzeri şekilde buda Property – TypeDef arasındaki iliskileri belirler. Yani hangi Property nin hangi TypeDef a ait olduğunu belirtir. Her bir PropertyMap rowunda Parent ve PropertyList olmak üzere iki kolon bulunur. Bu komonlardan ilki hangi TypeDef in Propertyleri için bu row un olduğunu PropertyList ise hangi indexden itibaren Propertylerin başladığını belirtir.

PropertyPointer = 0x16,
Property = 0x17 :

Tüm property tanımları bu table da olurlar. Bu table daki her bir property için Flags, Name ve Type olmak üzere 3 colon mevcuttur. Type colonu bir blob index belirtir. Bu blobdan okunan değeri çözerek bu Property nin Type ını ve parametrelerinin type’’larını bulabiliriz.

MethodSemantics = 0x18 :

Her bir event’in yada Property nin bildiginiz gibi içi aslında methodlardır. Yani

public int MyProperty
{
        get {return _MyProperty;}
        set {_MyProperty = value;}
}

şeklindeki bir property aslında iki ayrı methoddur.

public int get_MyProperty() {return _MyProperty;}

ve

public void set_MyProperty(int value) {_MyProperty = value;}

işte bu table bir property ile onun MethodDef lerini yada bir Eventle onun MethodDef leri arasındaki ilişkilendirmeyi yapmakta kullanılır. Her bir MethodSemantics row unda sırası ile Semantics, Method ve Association olmak üzere 3 colondan oluşur. Semantics flagları, Method methoddef tableında bri index i ve Association ise Event veya Property table’ında bir index belirtir.

MethodImpl = 0x19,
ModuleRef = 0x1A:


Bir assembly içinde kullandığınız DllImport lar nedeni ile hangi dll lere refernas taşıyorsanız onların herbirine burada bir row eklenir. Bu sayede DllImport çözümlerinde hangi modülden bu method’un çağrıldığını bulabiliriz. Her bir ModuleRef Name olmak üzere bir colondan oluşur.

TypeSpec = 0x1B:

Bir Type’ın signaturedan çözülmesini sağlamak için bir blob index belirtir. Bu blob’un çözümlenmesi ile ortaya bir Type çıkar. Her TypeSpec de bulunan rowlar sadece Blob’dan bir index barındırırlar.

ImplMap = 0x1C,
FieldRVA = 0x1D:


Static olan field tanımlamalarının değerlerini barındırır. Yani static yazdığınız field’ın cctor methodunda yüklenen ilk değerini tutar. Herbir FieldRVA rowu RVA ve Field olmak üzere iki kolondan oluşur.

EncodingLog = 0x1E,

EncodingMap = 0x1F,

Assembly = 0x20:

Bu dosya içinde tanımlı bulunan tüm Assembly’ler burada birer row şeklinde bulunur. Genelde birden fazla olmaz. Her AssemblyRow HashAlgId, Major, Minor, Revision, Build Flags, PublicKey, Name ve Culture olmak üzere 9 colondan oluşur.

AssemblyProcessor = 0x21,
AssemblyOS = 0x22,
AssemblyRef = 0x23 :

Assemblyniz içinde diğer assemblylere taşıdığınız her bir Assembly Referans’ı burada bir row şeklinde tutulur. Örneğin mscorlib.dll, System.dll gibi. Her AssemblyRef row unda sırası ile Major, Minor, Revision, Build, Flags, PublicKey Name, Culture ve HashValue olmak üzere 9 colon vardır.

AssemblyRefProcessor = 0x24:
AssemblyRefOS = 0x25:
File = 0x26:
ExportedType = 0x27:
ManifestResource = 0x28:

Her bir manifest resource için bir row buraya kaydedilir. Her manifest resource’un sırası ile Offset, Flags, Name ve Implementation olmak üzere 4 colon u vardır.

NestedClass = 0x29:

Nested classları tutmanızda yardımcı olurlar. Nested class’lar direk namespace içerisinde değilde bir class’ın altında bulunan classlardır. Aslında onlarda TypeDef de birer row olarka tutulurlar ancak burada onlarla onların Declaring Type ları arasındaki ilişkilendirme tutulur. Her bir NestedClass rowunda sırası ile NestedClass ve EnclosingClass olam üzere 2 colon vardır.

GenericParam = 0x2A:

Generic parametreler burada tutulurlar. Her bir Generic Parametre sırası ile Number, Flags, Owner ve Name olmak üzere 4 colondan oluşur.

MethodSpec = 0x2B:

Burada generic methodların Instantiation ları tutulur. Her bir MethodSpec row’unda sırası ile Method ve Instantiation olmak üzere iki colon vardır.

GenericParamConstraint = 0x2C:

Burada herhangi bir Generic Parametresinin Constraintleri tutulur. Herbir row Owner ve Constraint olmak üzere iki colon’dan oluşur.
----

Bu table’’ların hepsini detayları ile açıklarsak safalar dolusu döküman oluşur. Ben sadece burada birkaç tanesini açıkladım. Bunlar en önemli olanlar diyeceğim ama diğerleride bunlar kadar önemli sayılırlar. Ayrıca yine burada çok fazla kolonlarına değinmedim. Kolon çözümlemeleride aslında bazı durumlarda kompleksleşebilir. O nedenle çok girmeyeceğim bu konuya ama şu kadarını söyliyeyim işte MetaData Table lar dediğimiz şeyler bunlardır. Yani bir Assembly içindeki tüm herşey bu table’’larda tanımlıdır. Ve tüm bu table’’lar birbirleri ile ilişkilidir. CLR bunları çalışma zamanında ilişkilendirip buna göre çalıştırır.

Şimdi bu konuda çok kısa bir örnek yazalım daha detaylı olsun ve anlamaya yardımcı olsun; Bir Assembly’miz olsun ve içinde iki tane classımız tanımlı olsun

public class MyClass
{
       int _MyField = 0 ;
       Colors _Color = Colors.Red ;

       public MyClass()
       {

       }

       public string DoOperation(bool parameter)
       {
              if (parameter)
                     return “True”;
              else
                     return “False”;
       }

       [Browsable(false)]
       public int MyProperty
       {
              get {return _MyField;}
              set {_MyField = value;}
       }

       public Colors Color
       {
              get {return _Color;}
              set {_Color = value;}
       }
}

public enum Colors
{
       Red,
       Green,
       Blue
}

şimdi yazdığımız assembly’de sadece yukardaki tiplerin olduğunu düşündüğümüzde bu assembly’ nin meta data table’’ları şu şekilde oluşucaktır.

TypeDef;
Flags ........... Name ....... Namespace ...... Extends ..... FieldList .... MethodList
1                          MyClass*       MyNamespace*   0                        1                       1
1                          Colors*           MyNamespace*   1                       3                        0


TypeRef:

ResolutionScope ............. Name ................ Namespace
0x23000001                               Int32*                         System*
0x23000001                               Boolean*                    System*
0x23000002                      BrowsableAttrıbute*      System.ComponentModel*
....


MethodDef:

RVA .... ImplFlags .... Flags ... Name .......... Signature ... ParamList
...            0                          6               DoOperation*         3                        1
...            0                          6            get_MyProperty*      4                        1
...            0                          6            set_MyProperty*      6                        1
...            0                          6                 get_Color              12                       2
...            0                          6                 set_Color               24                      2
...            0                    0x806                .ctor*                    48                      2

Field:
Flags ................ Name ............... Signature
1                                _MyField*                          67
1                                _Color*                             45
1                                _Red*                               456
1                                _Green*                            33
1                                _Blue*                              97


Param:

Flags ............... Sequence ............ Name
1                                        0                           parameter*
1                                        0                           value*
1                                        0                           value*

Property:
Flags ............... Name ................ Type
0x200                   MyProperty*                    ...
0x200                         Color*                          ...

PropertyMap:
Parent .............. PropertyList
0x00000001                     1

CustomAttribute:
Parent .............. Type ................ Value
0x17000001      0x01000003                    ...

MethodSemantics:
Semantics ........... Method .............. Association
0x0002                       2                                      0x17000001
0x0001                       3                                       0x17000001
0x0002                       4                                      0x17000002
0x0001                       5                                       0x17000002

Assembly:
HashAlgId .. Major .. Minor .. Revision .. Flags .. PublicKey .. Name ....... Culture
0x8004                 1            1                 5345      0x8000          0            MyAssembly*        0

AssemblyRef:
Major .. Minor .. Revision .. Build .. Flags .. PublicKey ........ Name ........... Culture
2                 0                  0                 0             1  b77a5c561934e089* mscorlib*              0
2                0                  0                  0             1 b77a5c561934e089* System*                 0

Yanında * işareti olanlar aslında heap’’lerden okunan indexler olucak ama ben return değerlerini yazdım.

İşte yukarda yazdığımız basit bir Assembly Build edildikten sonra Meta Data Table ları yaklaşık olarak bu şekilde oluşacaktır. Listeyi elle hazırladığım için belki bir kaç ufak hata olabilir ama sonuçta bu tarz birşeyler ortaya çıkıcaktır.

Tabi bu yapı aslında daha da komplike bir şekilde olur ama ben örnek olsun diye basitce anlattım ve bu sırada birçok meta data table’ınıda yazmadım. Gerçekte ise oluşturduğumuz type lar, referansları, attributeları, methodlar, parametreler, düşünüldükçe bu yapının nasıl bir hal aldığı daha çok anlaşılacaktır.

Kısaca yukardaki gibi çözdüğümüz meta data sayesinde elimizde bu assembly’nin tam çözümlenmiş Type ve Member yapısı oluşur. Yani bir nevi Reflection la çözülen yapının aynısı.

Şimdi sıra bir methodun decompile edilmesine geldi. Bir methodun decompile edilebilmesi için o method’un Body’sini yani methodun iç kısmını okumamız gerekir. Bunun için Method’un RVA’sına gidilir ve okuma yapılır. Okuma işlemi method’un büyüklüğüne göre iki formatta olur. FatFormat (buyuk methodlar, try catch veya local variable barındıran methodlar) ve Tiny format. Tiny formatta method da sectionlar yoktur ve Code’lar buradan okunur. FatFormatta ise methodun kodu birden fazla section’a bölünür ve oradan sırası ile okunur. Aslında try catch kullandığınızda yada variable kullandığınızda method otomatikman FatFormatta olur. Çünkü TinyFormatta try catch ve Lacal Variable yoktur.

Method RVA’sını o RVA yı kapsayan sectiondan bulup o position’a gidip oradan Method Body’sini okuduğumuzda elimizde byte[] şeklinde bir data oluşacaktır. Okunan bu byte[] şeklindeki data o methodun core datasıdır. Şimdi bunu işlemek gerekir. Aslında okunan bu byte[] tum IL datasıdır. Bunun için once IL’yi biraz inceleyelim. IL opcode lardan oluşur yani bir komut gibi düşünürsek bir işlemi ifade eden bir data ve eger varsa bunun aldığı bir data sonrasındada o data olucak sekilde dizilirler. IL de her bir opcode’un bir Operand Type’ı vardır.

Bu Operand Typelar;

OperandType.InlineNone: Operand dan sonra herhangi bir data olmaz. Sıradaki Operand’a geçilir.

OperandType.ShortInlineBrTarget: Operand dan sonra bir sbyte lık data target belirtir. Bu operand tipi branch yani zıplama operand larında olur ve zıplamanın olacağı adresi belirtir. Yani goto ... gibi düşünebilirsiniz.

OperandType.InlineBrTarget: Operand dan sonra 4 byte lık bir integer data target belirtir. Bu operand tipi branch yani zıplama operandlarında olur ve zıplamanın olacağı adresi belirtir. Aynı ShortInlineBrTarget gibidir ancak tek fark gidilcek adres 4 byte la ifade edilir.

OperandType.ShortInlineI: Operand dan sonra bir sbyte lık data vardır.

OperandType.InlineI: Operand dan sonra 4 bytelık bir integer data vardır

OperandType.InlineI8: Operand dan sonra 8 bytelık bir long data vardır

OperandType.ShortInlineR: Operand dan sonra bir single data vardır

OperandType.InlineR: Operand dan sonra bir double değer vardır

OperandType.InlineString: Operand dan sonra 4 bytelık bir integer string’in US Heap deki index’ini belirtir. Burada önemli olan bu index’in ilk 20 bit’i kullanılır. Yani (reader.ReadInt32() & 0x000fffff) le dönen değer bize US Heap’deki index’i verir. Buradan okunan indexdeki string değeri bu operand için data olarak kullanılır.

OperandType.ShortInlineVar: Operand dan sonra bir byte lık data vardır

OperandType.InlineVar: Operand dan sonra 2 bytelık unsigned short değer okunur. Bu değer bir index belirtir. Bu index bu method içinde tanımlanan local variable indexidir. Yani sonuçta bu index’deki variable bu operand’in datasıdır.

OperandType.InlineSig:
OperandType.InlineMethod:
OperandType.InlineField:
OperandType.InlineType:
OperandType.InlineTok:
Operand dan sonra 4 bytelık bir int değer okunur. Bu int değer aslında bir tokendir. Bu token’in ilk byte’ı bize hangi MetaTable olduğunu bulmamızı sağlar ve son 3 byte’ı ise o tabledaki index değerini verir. Yani;

int token = reader.ReadInt32();
int table = ((token & 0xff000000) >> 24);
int index = (token & 0x00ffffff);

buradan çıkan sonuca göre örneğin table = 1 ve index = 1 ise bu bize; 1 nolu table in 1. item’ı demek oluyor. Buda 1 nolu table TypeRef ve bunun 1. item’ı. Yalnız unutmamak gerekirki MetaData Table larda indexler 1 den başlar. Bu nedenle 1. item demek aslında 0. index deki itemdır. Yani burada bu Opcode un datası TypeRef table’ının 0. index deki TypeRefdir.

OperandType.InlineSwitch: Operand switch operandıdır. Yani switch ve bunun case leri belirli bir mantıkda sıralanır. Bunun cözümlenme mantığı ise ilk 4 byte bu switch’in kaç case’i olduğunu gösterir. Daha sonra bu case sayısı kadar her case’in target’ini yani hangi ofset’e zıpladığını belirten 4 bytelık targetlar vardır.

int[] cases = new int[reader.ReadInt32()];

for (int i = 0; i < cases.Length; i++)
      cases[i] = reader.ReadInt32();

Buradan çıkan sonuca göre cases array şeklinde switch in case lerini belirtir ve bu operandın datası olur.

Bu operand tiplerine göre body den okuduğumuz code’u çözerir.

Yani code array de donerken sırası ile önce opcode’u okuruz. Sonra bu opcode’un OperandType ına göre sonraki datasını (varsa) okuruz ve sonra yeni bir operand’a geçeriz. Burada önemli bir diğer nokta ise operandlardır. .NET’te iki tip operand vardır. Bunlardan ilki tek byte’lık operandlar diğerleri ise 2 byte’lik operandlardır. Bunun için önce bir byte okuruz eger bu byte 0xFE degilse bu demekki o tek byte’lık bir operand’dır. Yok eğer bu byte 0xFE ise bu durumda o operand’ı çözmek için sonraki byte da okunur ve ilk okunan ile orlanır.

byte operand = reader.ReadByte();
if (operand == 0xfe)
{
        byte next = reader.ReadByte();
        operand = next | 0xfe;
}

Bunu yaptikdan sonra da yukardaki gibi operand datası okunur.

Şimdi şu ana kadar biz bir dosyayı aldık onun headerlarını okuduk, sonra onun meta data table larını okuduk ve şimdi de bir method bodysini okuyup onun opcodelarını yani IL kodlarını çıkarttık.

İşte bundan sonra asıl zor kısım yani onun CodeDom’unu yani expression ve statementlarını oluşturmak kalıyor. Burası konusunda cok bilgi veremeyeceğim. Ama burada önemli olan her opcode türü için yapılacak işlemler ve bunların stack da tutulmasını yaparak bir geri cözümleme yapmak. Bu ilk çözümlemede ortaya çıkan kod çok okunabilir birşey olmayacaktır. Daha sonra compilerın ürettiği kodları düşünerek tersi işlemle anlamlı okunaklı bir kod üretilmesi gerekir.


Örneğin bir branch gördüğümüzde eğer bu contional branch’sa bunu if, while, do while, for gibi bir şeye uydurmak gibi çözümleme yapılmalıdır. Dahada detaylandırcak olursak;

Örneğin kodumuz şu şekilde olsun :

L_0043: br.s L_0072
L_0045: ldarg.0
L_0046: ldfld ......
L_004b: ldarg.0
L_004c: ldarg.0
……………………..
L_006e: ldloc.0
L_006f: ldc.i4.1
L_0070: add
L_0071: stloc.0
L_0072: ldloc.0
L_0073: ldarg.0
L_0074: call …
L_0079: callvirt …
L_007e: blt.s L_0045
L_0080: ldc.i4.0

gibi bir kodumuz olduğunu düşünürsek burada 0043 deki br.s yani branch short bir Branch operand dır ve sonrasında 1 bytelık bir daha (target) alır. Şimdi o kod bize L_0072. ofset e zıplamamızı söylüyor. Oraya gidip baktığımızda onun öncesinde 006e den itibaren

ldloc.0 -> 0. local variable i evaluation stack a yükle)
ldc.i4.1 -> Int32 olarak 1 sayısal değerini evaluation stacke koyar
add -> İki değeri toplar ve sonucu evaluation stacke koyar
stloc.0 -> Stackdan bir değer Pop eder ve onu 0. local variable’a atar

Şimdi burada yapılan işlem aslında şunun gibi birşey , a diye bir sayısal değişkenimiz olsun ve bu bizim local variable larımızın 0. index dekisi olsun. Yani ilk tanımlanan Local variable. Bu kod bu durumda şu demek oluyor.

a = a + 1;
ki buda
a += 1;
ki buda
a++; ‘le aynı şeydir…

Gördüğünüz gibi o zıplamanın adresinden önce bir i++ işlemi oluyor. L_0072 den aşşağıya doğru devam ettiğimizde ise yine 0. variable ın 0073 de stack e yüklendiğini görüyoruz. L_007e de ise bir conditional zıplama görüyoruz. blt.s L_0045 -> eğer ilk değer diğerinden küçükse calışır. Yani ilk değer diğerinden küçükse L_0045 e geri dönüyor.

İşte bu şekilde kodu detaylı incelediğimizde ortaya şu çıkıyor;

for (int i = 0; i < …Count; i++)
{
      …..
}

gibi bir kod. İşte bu şekilde yapılan dünya kadar işlem sonucunda IL kodu ancak anlaşılır bir kod çıkartır ortaya.

Burada bir diğer önemli nokta ise .NET teki tüm statement ve expressionlara çok iyi hakim olmak gerekir. Yani tüm expressionların nasıl çalıştığını output un ne olduğu gibi.

Örneğin;
lock statement’ının aslında System.Threading.Monitor.Enter la başlatılan bir monitor ve System.Threading.Monitor.Exit lede çıkıldığını bilmek gerekirki bu method’a bir call olduğunda otomatikman lock statement oluşturulmalı.


Yada foreach’in aslında IEnumerator ve IEnumerable la alakalı olduğunu ve bunun mantığının .. IEnumerator enumerator1 = obj.GetEnumerator();
try
{
       while (enumerator1.MoveNext())
       {
              int data = (int)enumerator1.Current;
              /////
       }
}
finaly
{
       if (enumerator1 is IDisposable)
       {
              ((IDisposable)enumerator1).Dispose();
       }
}

şeklinde olması gerektiğini bilmek gerekir.

Bir diğer önemli nokta. CodeDom’un çok iyi oluşturulması gerekir. Yani tam bir modelleme yapılması gerekir. Sonuçlar ortaya modellenmiş şekilde çıktığında ise artık geriye tek bir iş kalmış olucak ki oda bu model’i render etmek. Bunun içinde her dil için bir renderer yazmak yeterli olucaktır. Ani C#, VB.NET gibi. Bu dillerin herbirinde bu modellenmiş kodlar farklı şekilde çıktı verir.

Burada kod model’e bir örnek olarak isterseniz if, else kalıbını verelim.

public interface IConditionStatement : IStatement
{
      IBlockStatement Then {get;set;}
      IBlockStatement Else {get;set;}
      IStatement Condition {get;set;}
}

yine bir diğer kod model objesi olan SizeOf’’uda verelim...

public interface ISizeOfExpression : IExpression
{
      ITypeName Type {get;set;}
}

İşte bunun gibi varolan tum expression ve statementlarin codemodelini oluşturmak çok önemli. Çünkü biz decompile ederken bu codemodeli oluşturuz ki bu sayede elimizde codedom oluşmuş olur.

Daha sonra renderer her expression ve statement’ı yeri geldiğinde render ederiz.

Örnegin C#Renderer SizeOF Expression’ı gördüğünde sizeof(expression.Type) şeklinde bir işlem uygular.

Sonuç olarak Fox’un hikayesine değindikten sonra nasıl decompile yapılabileceğini az çok göstermiş oldum. Umarım anlaşılır olmuştur. Aslında bütün bunlar olayın sadece yüzeysel bir anlatımı. Bu konuda uğraşmak isteyenlerin daha detaylı çalışma yapmaları gerekecek. Çünkü her bir meta data table’ının kendine ait özel specific durumları bunların çözümü, yaklaşık 50 civarında expression, 10 civarında Type bütün bunlar hepsi ayrı ayrı çözümlenmeli. Bütün bu anlattıklarımdan sonra umarım herkes .NET’in ve CLR nin hangi mantıkla çalıştığını az çok anlamışlardır. Bizim yaptığımız aslında CLR’nin yaptığına benzer birşey olduğu için engellemenin çok fazla yolu yok.

Ancak yinede herkese kodlarını korumak için ellerinden geleni yapmalarını tavsiye ederim. Şu anda bir çok kod koruma aracı mevcut. Bu kod koruma araçlarından hangisinin daha uygun olduğunu belirlerken asla unutmamanız gereken şey ise Obfuscation’ın bir işe yaramayacağı. Çünkü obfuscate etmek sadece ama sadece rename etmektir. Yani örneğin bizim TypeDef table’ındaki bir TypeDef’i okuduğumuzu varsayalım. Obfuscate işlemi uygulandığında USHeap ve StringHeap deki stringler obfuscate edilip _1, _2 yada a, b gibi birşeyler olucak. Yani sadece String ve USHeap de bir şeyler değişicek. Zaten decompiler araçları bunları sadece ama sadece isimleri gösterirken kullanıyor. Yukardada söylediğim gibi asla textler üzerinden işlem olmaz. Bir TypeDef’in name ini okurken bize sadece bir index değeri gelir. Bu StringHeap deki bir index dir ve gidip oradan o text’i alır yazarız. Onun haricinde o TypeDef’i cağıran hiçbir yerdede bu isimle işlem yapılmaz. Direk TypeDef table’ında o TypeDef’in indexi ile işlem yapılırlar. İşte bu nedenle yapmanız gereken koruma tool’unuzu seçerken obfuscate etmesine göre değil exktra özelliklerine göre seçmeniz. Bu konuda benim tavsiyem Fox’u satın alan Xenocode’un diğer tool’u olan PostBuild’dir. PostBuildde .NET kodlarınızı direk Native koda çevirebilme şansınız var. Bu sayede artık ortada MetaData lar kalmadığı için Fox yada Reflector’un çözmeside mümkün olmayacaktır.

Hepinize kolay gelsin iyi çalışmalar.

Özcan DEĞİRMENCİ
odegirmenci@hotmail.com


 
  • Yazılan Yorumlar
  • Yorum Yaz
AĞU
22
2006
Sayin ayhan turgud. Birakin .NET i gibi bir kullanim gordum. Gercekten cok yanlis bir yaklasim. Eger .NET le derimden ilgilenirseniz ve bilgi sahibi olursaniz bu tip evhamlara gerek olmadigini eminim goreceksiniz. Sonucta Fox da C# la yazildi. eger o derece korkulacak bir sey olsa idi emin olun baska bir dille yazardik.. Iyi gunler iyi calismalar
MAY
25
2006
tebrikler. ii güzel, hoş bir uygulama olmuş. peki o zaman biz niye .net teknolojisini kullanıyoruz? ki FOX gibi bir uygulama madem kodu sln dosyasına kadar reverse edebiliyorsa uygulama geliştirmenin manası kalmıyor ki.EY .NET GELIŞTIRICILERI,BIRAKIN IŞINIZI,EMEĞINIZI ALTIN TEPSIDE SUNMAYIN!!!
NİS
25
2006
Başta Özcan Beyin eline sağlık diyorum.Cesaret ve özveri isteyen bu çalışmanızı gerçekten mükemmel buldum.Fakat bazı arkadaşlarım belki bana kızacak ama sadece merakımdan dolayı soruyorum art niyet yok.Ama proğramda bulunan çarkların renkleri kendi seçiminiz mi?Başarılarınızın devamını diliyorum.
NİS
21
2006
Bilgi ve tecrübelerinizi paylaştığınız için teşekkürler. Programcılar için örnek bir çalışma olduğuna inanıyorum.
NİS
17
2006
Şimdi .Net ile yazdığımız programlar çözülebiliyo yani doğrumu anladım?
NİS
5
2006
Gurur duydum, çok tebrik ederim.
MAR
29
2006
Iyi gunler, Obfuscator yazilmasi daha kolay olan bir seydir cunku method body ile dogrudan isiniz olmadigi icin IL ile muhatap olmazsiniz. Tek yapmaniz gerkeen Meta Data Table larini okumak ve onlarda gerekli isim degisikliklerini yapip dosyayi yeniden olusturmaktir.
MAR
27
2006
Oncelikle tebrik ederim. Bilgilerinizi paylasmaniz gercekten sevindirici. Acaba bir de saglam Obfuscator yazilabilir mi?
MAR
27
2006
Fox un ilk çıktığı zamanlar C#nedir.com da baya zaman harcıyordum ama sonrasında şartlar buna fazla müsade etmedi.ilk sürümünü denemiştim ... o zamanlar sorunlar elbette olacaktı ama bugün Xenocode aldığını duyduğumda gerçekten çok sevindim. Özcan abi , tebrikler. mükemmel bi iş
MAR
26
2006
Bu çalışmaların olması belki .NET i zamanla daha güvenilir bir derleyici ortamının oluşmasına sevk edecek fakat tahminim ticari firmalar güvenlik önlemleri artırılmazsa programlarını yazmada .NET ortamından zamanla uzaklaşacaktır. Özcan hocamızın bazı DLLleri çözerek sorunları çözmesi tabiki yararlı fakat kimse bu yararların yanısıra, güvenlik sorunlarını gözardı edemez. Sonuçta bir program çıkarmak için o kadar mühendis çalışıyor, o kadar harcamalar yapılıyor ve emek veriliyor. Eğer aksi savunuluyorsa etik olarak açık kaynak kodlu programlardan faydalanılmalıdır diye düşünüyorum
MAR
23
2006
Gerçekten çok sevindim böyle üst düzeyde bir ürün çıkarılmasından onur duydum.Ama keşke ülkemizin bir markası olarak dünya piyasalarında yer alsaydı.Şu ana kadar hep taşeronluk yaptık.İnşallah bir ileri aşamamızda da dünya markalarımız olur.Yürekten kutluyorum.
MAR
22
2006
Çok güzel bir çalışma, gerçekten tebrik ederim. Özellikle fazla kısıtlı bilgiler ışığında bunu başarabilmen gerçekten hoş. Sorması ayıp ne kadara attın bu uygulamyı bu konuda da bilgi verebilirmisin :)
MAR
20
2006
Helal olsun özcan. Başarılarının devamı dilerim. Çalışmaların gayet güzel ve bizleri gururlandırdı. Yürekten kutlarım
MAR
17
2006
Bende azmi ve caliskanligindan dolayi özcan arkadasimi tebrik ediyorum. Gercektende istenilirse neler yapilabilecegini gösterdi. Herseyin hazirini disaridan bekleyip kullanmak yerine daha iyilerini biz türk yazilimcilarininda yapacagini ispatladi. Özcan in daha önemli basarilara imza atacagina ve bu sayade türk yazilimcilarinin ufuklarini acicagine ben eminim... Hayallerin kadar büyüksün...
MAR
14
2006
Gerçekten tebrik ederim.Türk yazılımcının sektörde dünya piyasasına çıkması bana ve kodyaziyorum.com ekibine gurur verdi.Başarılarınızın devamını dilerim.
Sayfalar : 1 2 
Yorum yazabilmek için üye girişi yapmalısınız. Üye girişi için tıklayın.
Üye değilseniz Üyel Ol linkine tıklayarak üyeliğinizi hemen başlatabilirisniz.
 
SON 10 Haber
» Uygulama Geliştirme Yarışması
» Microsoft Yazılım Teknolojileri
» BT Akademi Eğitim Takvimi
» BT Akademi Ankara'da
» BT Akademi’den MCPD Eğitimi...
» MVC Eğitiminde Kampanya
» İş İlanı : Avenn Group
» IT Proje Yönetimi
» ASP.Net MVC Framework Semineri
» Silverlight 3.0 Programı