Tersine Mühendislik ve Zararlı Yazılım Analizi – 1

Yeni bir yazı dizisine başlıyoruz. Bu yazı dizisinin amacı özellikle “tersine mühendislik” ve/veya “zararlı yazılım analizi” ile ilgilenen, bu alana başlamak isteyip de nerden başlayacağını bilemeyenler için yardımcı bir kaynak olmaktır. Anlatım her zaman basit (Tabii ilerleyen zamanlarda önceki yazıları okuyup anladığınızı varsayarak biraz daha teknik  detaylar ön plana çıkacaktır.) ve akılda kalacak şekilde olacaktır. Sorularınız olursa parmak kaldırmayı beklemeden yorumda sorularınızı belirtebilirsiniz. Fazla uzatmadan başlayalım…

Öğrenmeye  çalıştığımız konular hemen bir iki uygulama ile öğrenilecek konular olmadığından, yazımızın ilk kısmını “Bilgisayar Yapısı ve Assembly” konularına ayırıyorum. Neticede yapacağımız işlem arka planda ne olduğunu öğrenmeye yönelik olduğundan , bizlere de düşen sistemin nasıl çalıştığı hakkında bilgi sahibi olmak.

Bilgisayarın başına geçtiğimizde açtık  bir text editör ve klavyeden  ‘V’ tuşuna bastık.  Bu harfi editörümüzün üzerinde gördük. Bu olay , normal bir bilgisayar kullanıcısı için sıradan bir durum olsa da arka planda yapılan işler hiç de öyle sıradan görünmüyor. Tabii bu örneği verip hemen  ‘F-D-E’ nedir anlatmayacağım. Önce kaba taslak bir bilgisayar modeline bakalım.
 

Yandaki resimde 4 ana bölüm görüyoruz. Bunlardan kısaca bir bahsedelim;
1-Input : Bu kısımda veri giriş cihazları ve yöntemleri mevcut. Örneğin bir klavye, mikrofon…
2-CPU: Bu kısımda veriler işlenir ve ilgili yerlere aktarılır.(Şimdilik böyle masum bir tanımla bırakalım  )
3-Memory:  Bazen çok fazla verimiz olur. İşlenip değerlenene kadar bir yerlerde tutulması gerekir veya bunun gibi işler (Bu da böyle kalsın  )
4-Output: Yaptığımız işleme göre içeride işlenen değerlerin son hali bize bu kısım sayesinde iletilerek , yapılan işlemin kullanıcı gözünde değerli olmasını sağlar.
Bu tanımı yaptıktan sonra  1 ve 4 numaralı bölümlerden kendimizi biraz soyutlayıp özellikle CPU ve Memory  arasındaki ilişki ve herbirinin iç yapısına daha detaylı bakabiliriz.(Uzay mekiği kalktığına göre birkaç modül bırakmak zorundayız…)

Nedir bu CPU …

Açılımı “Central Processing Unit”, bizim dilimizde “Merkezi İşlem Birimi”. İçerisindeki arabirimler ve modüller sayesinde veriler üzerinde aritmetik ve mantıksal  işlemler yapan bilgisayar üzerinde  çalışan uygulama süreçlerini yöneten birimdir
Genel yapısı şu şekildedir;

Bir birimin iç yapısında artık yeni birimlerle karşı karşıyayız.  Şimdi bunların ne işe yaradığına bakalım genel olarak. Daha sonra her bir birimi ayrıntılı bir şekilde ele alırız.
1-    Control Unit : Bu birim sayesinde CPU içerisinde işlenen veriler ve süreçler takip edilerek organize bir biçimde verinin sağlıklı bir şekilde birimler arasında dolaşmasını sağlanır. Bu birimi trafik polisine benzetebiliriz. Hangi araçların ne zaman ne yönde geçiş hakkı olduğunu belirlemesi ve yönetmesi karmaşık bir yapının düzenli bir hâl almasını sağlar.

2-    ALU: Açılımı Arithmetic Logical Unit. Adından da anlaşılacağı üzere bu birim aritmetik ve mantıksal işlemlerin yapıldığı bölümdür.  Yapılan bütün işlemlerin temelinde aslında toplama işlemi vardır. Örneğin bir sayının başka bir sayı ile bölünmesine bakalım. Bölme işlemi aslında bir çarpma işlemidir, çarpma işlemi ise toplama işlemi… Mantıksal bir karşılaştırmayı ele alalım. (5==4) gibi burada yapılan işlem çıkarma işlemidir. 5-4=0 ise iki sayı birbirine eşittir değilse eşit değildir. Yapılan işlem bir çıkarma işlemidir ve çıkarma işlemi de aslında bir toplama işlemidir.

3-    Registers: Türkçeye çevirince anlamını kısmi olarak yitirse de isterseniz  “yazmaçlar” veya “kaydediciler” de diyebilirsiniz. CPU içerisinde işlenen verilerin tutulduğu alanlardır. Yapı olarak  programlama dillerindeki değişkenlere benzerler.  Bizim işimiz ilerleyen zamanlarda hep bu alanlarla olacak. Kullanılan mimari yapısına göre değişik sayılarda ve görevlerde olurlar. Örneğin MIPS mimarisinde 32 adet register bulunur ve bunlar kendi aralarında R-Type , J-Type … gibi gruplara ayrılmıştır.(Nerden çıktı bu MIPS demeyin. İlerleyen zamanlarda ona da ayrıntılı değineceğiz ) Registerların yaptığı işleme basitçe şöyle bir örnek verelim. Biz 2 ve 3 sayılarını toplayacağız zaman 2 ve 3 bu registerların içinde saklıdırlar. ALU bunları alır toplar ve yine kullanmak üzere bu registerlardan birine gönderebilir.

4-    Signals: Bir de bazı sinyaller var şeklimizde. Bunları şu şekilde somut bir örnek ile belirtelim. Trafik polisi örneğimize dönersek, trafik polisi  herhangi bir yönde bir araca geçiş hakkı veya başka bir şey yapma isteğini el hareketi ve düdük kombinasyonu ile belirtir. Her bir hareketin özel bir anlamı vardır. İşte sinyalleri bu hareketlere benzetebiliriz. Bu sinyaller sayesinde süreç içerisindeki birimler uyarılarak işlemin bir düzen içerisinde gerçekleşmesi sağlanır.

5-    Bus: Ayrıca bus diye bir kavram görmekteyiz. Bunu da yine trafik örneğimiz üzerinden anlatacak olursak, araçların trafikte kullandığı yolları bus’ a benzetebiliriz. Aynı şekilde verilerin birimler arasında iletilmesi bu yollar üzerinden gerçekleşir.

Burda anlatılan her birim ve kendi aralarındaki ilişkilere detaylı bir şekilde inmeden önce bir de memory’den bahsedelim.
Memory
Türkçe ismi ile “bellek”… Hücresel bir yapıdan oluşmuştur. Bu hücrelere veriler kaydedilir.(Dikkat ederseniz henüz verinin ne tipte olacağını daha konuşmadık.)  Kendi içinde farklı amaçlara hizmet etmek için farklı bölümlere ayrılmıştır. Aşağıda gösterilen resimde bunu görebilirsiniz.

Bu alanlar CPU içerisindeki  birimler gibi kesin somut olan bölümler değildir. Mantıksal bölümler olduğundan işletim sistemi, kernel… gibi parametrelere bağlı olarak değişkenlik gösterebilen alanlardır. (Tabii işletim sisteminin bize verdiği bilgi bu, ilerleyen zamanlarda bu konuda elimizin bağlı olmadığını bu alanlar üzerinde söz hakkımızın olduğunu göreceğiz…) Burada bilmemiz gereken ve önemini kavramadan bundan sonra anlatılan herşeyin anlamsız kalacağı bir tanımımız var. “Adres” …
Bizim işimiz veri ile demiştik ve örneklerimizde hep verinin işlenmesi, verinin bir yerden bir yere taşınması gibi eylemlerden bahsettik. Peki bu verilere nasıl ulaşılıyor? Ya da soruyu şu şekilde soralım. Bir birim(ALU-CU-CPU…)  işleyeceği verinin tam olarak nerden getirileceğini nerden biliyor? İşte tam bu noktada adres kavramı önem kazanıyor.  Assembly konusunda da göreceksiniz ki işlenecek her bir veri nerde olursa olsun, hangi birimle çalışırsa çalışsın tabir yerindeyse bir ikametgah adresi almak zorundadır. Bu ikametgah adreslerini bilen bir memur gerektiği zaman, gereken kişilere bu adresi vererek ilgili alana erişimin yapılmasını sağlar. Assembly kodlarının işletilmesi aşamasını anlattığımızda bu durum daha da netlik kazanacaktır. (Little-Endian,Big-Endian gibi kavramlar ilerleyen bölümlerde adres konusu detaylı bir şekilde ele alındığında anlatılacaktır. )

Eee… Buraya kadar bir şeyler öğrendik. (veya tekrar ettik) Dikkat ederseniz bütünden parçaya doğru ilerledik ki daha da ilerleyeceğiz. Özellikle register’ların iç yapısına değinince bu işin transistörlere kadar varacağını anlayacağız. Her ne kadar alt parçalara insek de büyük resmi hiçbir zaman kaybetmemeliyiz. Buraya kadar anlatılanlarla aşağıdaki resim daha da anlamlı gelmiştir umarım.

Registerların detayına inmeden önce genel olarak bir programın çalışma prensibini anlatmak istiyorum. Bunu da anlatmadan önce özel bir register olan PC(Program Counter)’ den bahsetmem gerekiyor.
PC özel bir register olarak geçer. Yaptığı iş bir program işletilirken program içerisindeki bir sonraki talimatın (instruction) adresini tutar. Şöyle düşünelim nüfus sayımı için bir mahalleye giden memurlar, rastgele evlere gitmezler. Şu anda bir evde sayım yaparken bir sonraki evin hangisi olacağı ellerindeki kağıtta yazılı olur. Yani bir sonraki adım önceden bellidir ve sayım yaptıkları ev bittikten sonra bu kağıtta yazan adresteki eve giderler. Bundan sonra ise hangi eve gideceklerini ellerindeki kağıtta güncellerler ve bu aşamadan sonra da hangi eve gidecekleri önceden belli olur. İşte PC’nin de yaptığı iş tam da bu şekildedir. Bir komut çalışırken bir sonraki komutun adresi bu register içerisinde tutulur. İlgili komut işletildikten sonra bu register içerisindeki adrese gidilir ve ilgili komut getirilir. Hemen ardından bundan sonraki komutun adresi ile bu register güncellenir. Bu işlem programın çalışması bitene kadar bu şekilde devam eder.(Genel çalışma prensibini anlamanız şu anda yeterlidir. İlerleyen bölümlerde mecburen detaylanması gereken özel bir durumdur zaten.)

Şimdi öncelikle aşağıdaki resme bakalım. Zaten şu ana kadar öğrendiklerimiz doğrultusunda bize mantıklı gelecektir.

Resimde bellek ve CPU arasında bir veri alışverişi durumunu gözlemleyebiliriz. Bellek kısmındaki her bir hücrenin kendine ait bir adresi vardır. Ayrıca bu hücrelerin içinde bazı değerler vardır. Bu değerler normal bir sayı değeri olabileceği gibi, bir komutun  ikili sayı sistemindeki karşılığı da olabilir. (Bir makine kodunun ikili sayı sistemindeki karşılığının nasıl bulunacağını anlatacağız).  Location X şeklinde belirtilen alanlar adres alanları ve bu adreslerde aslında ikili sayı sistemi ile gösterilir.  Bu kadar gözlemden sonra asıl konumuza  geri dönelim.

Diyelim ki, bilgisayarımızdan  IDA’yı açtık.(IDA ileride bol bol kullanacağımızdan şimdiden bilinçaltınıza aşılasam iyi olur ) Bunu herhangi bir program olarak da  düşünebiliriz. Bunu açar açmaz bu program bir process  halini alır. Bu şu demektir; siz ikinci defa aynı programı açarsanız, her iki uygulama aynı programa ait olabilir ama arkaplanda bunlar farklı işlemler olarak algılanır ve ona göre değerlendirilir. İşte arka planda bunun adı process  oluyor. Bu process ile ilgili veriler belleğe “loader” adı verilen bir yapı sayesinde ilgili alanlara yüklenir. Bu yüklenme şu şekilde oluyor. Çalışacak instruction’lar  bellekte ilgili alanlara ikili sayı sistemindeki karşılıkları ile yüklenir. Program çalışacağı zaman PC bu programın çalışacağı başlangıç noktasının adresini tutar ve bu adresi ilgili birimlere aktararak bu adresteki komutun bellekten getirilip (FETCH) ardından komutun anlaşılır bir hale gelmesi için çözülmesini (DECODE) ve sonrasında çalıştırılmasını (EXECUTE) sağlar. Bundan sonra PC bir sonraki instruction’ın adresi ile günlenerek programın çalışmasını devam ettirir. Bundan sonra da gerekirse veriler tekrardan belleğe yazılır.
Bu aşamadan sonra biraz daha detaylara inelim. Biraz önce FETCH-DECODE-EXECUTE dedik ama işler bu kadar da  kolay olmuyor. Bu aşamalarda önemli roller ve bu rollerin aktörleri var elbette. Bu noktada verilen rollerin dağılımını daha iyi anlamak için daha önceden tanımını yaptığımız register’lara biraz daha detaylı değinelim.
Not: Buradan sonra aksi belirtilmediği sürece IA x86 mimarisi üzerinden gideceğiz. Tabii ki de her konunun sonuna (eğer gerekirse) özellikle MIPS ile ilgili karşılıklarını da belirteceğiz. Zaten genel mantığı kavradıktan sonra  çok da farklı şeyler olmadığını göreceksiniz.

Register…

Aşağıdaki resimde genel olarak var olan registerları görebiliriz.

Ne kadar da fazla değiller mi? Ama bizim amacımız bir sonraki aşamada yapacağımız işlemlere hazırlıklı olmak olduğundan şu anda işimize en çok yarayanlara bakacağız. Zaten serinin devamında mecburen diğerlerini de görmüş olacağız.

Her bir register içine 32 bitlik bir veri alabilen özel yapılardan oluşmuştur.  Her biri normalde belirli bir amaca hizmet için vardır. Fakat isimleri dışında kendi aralarında pek bir fark olmadığından (bazı özel register’ları bunun dışında tutuyorum) birbirlerinin yerine de kullanılabilir.

Registerlarımızı  tanımadan evvel isimlerinden bağımsız birkaç özelliği açıklayalım. Yukarıdaki resimde EAX register’ını ele alalım. Bunun 32 bit olduğunu belirtmiştik. Bu register’ın ilk 16 bitine AX denir. Yani birazdan assembly konumuzda bizler EAX’e değil de AX’e bir şeyleri yüklüyorsak bu şu anlama gelir;
Biz  ilgili veriyi bu register’ın ilk 16 bitlik alanına kaydediyoruz. Bu ilk 16 bitlik alanda kendi arasında 8’er bitlik iki alana ayrılıyor. İlk 8 bitlik alana AL ikinci 8 bitlik alana ise AH denir. (AL-Low, AH-High). Tabii buna ne gerek vardı diyeceksiniz. Bunlar var olan alanın daha tasarruflu kullanılması ve daha hızlı işlem yapmak için varlar.(İleride Optimizasyon konusunu anlatmaya zamanımız olursa  bunu daha iyi anlarsınız)
Not: 64 bitlik bir mimaride bu 32bitlik registerların yanına aynı boyutta yeni bir register konulmuştur. Örneğin EAX burada RAX diye adlandırılır. Daha sonradan bunu da sürekli ikiye bölerek yukarıdaki şekli yine elde edebiliriz. Aşağıdaki resmi bunun için inceleyebilirsiniz.
 
|__64__|__56__|__48__|__40__|__32__|__24__|__16__|__8___|
|__________________________RAX__________________________|
|xxxxxxxxxxxxxxxxxxxxxxxxxxx|____________EAX____________|
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|_____AX______|
|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|__AH__|__AL__|
Artık register’larımızı biraz daha yakından tanımanın vakti geldi. Fakat bunu serimizin 2. yazısında  detaylandıracağız. Fakat aşina olmamız açısından şimdilik şu ufak tanımlar yeterli gelecektir;

1-    EAX : Accumulator  Register diye okunur. Genel olarak aritmetik işlemlere girecek veriler buraya kaydedilir.(ADD,SUB,MUL…gibi)
2-    EBX: Base Register diye okunur. Dolaylı Adresleme de kullanılır.(Assembly’de bellek alanlarına erişimde anlayacaksınız bu konuyu)
3-    ECX: Count Register diye okunur. Özellikle tekrar gereken yerlerde sayaç olarak kullanılır. (LOOPING,SHIFTING…)
4-    EDX: Data Register diye okunur. Genelde uygun bir alanda veri tutmak için kullanılır.  Bunun dışında diğer register’lar işlem yaparken onlara yardım etmek için de kullanılır.

Bunun dışındaki register’ları yeri geldikçe anlatacağız zaten.
Bir sonraki yazıda görüşmek üzere…