SlideShare a Scribd company logo
1 of 183
X86 TERSİNE
MÜHENDİSLİK
EĞİTİMİ
BÖLÜM-2
İSTANBUL
blog.btrisk.com @btrisk /btrisktv /btrisk
OYUN
1. AŞAMA
ÖN BİLGİ
• Birinci aşama eğitmen eşliğinde gerçekleştirilecektir. Bu çalışma
sırasında IDA'nın temel özellikleri ve kullanımına aşina olmanın
yanı sıra tersine mühendisliğe uygulamalı bir giriş yapılacaktır.
• Birinci aşamaya kısa bir "dinamik analiz" ile başlayacağız.
• Daha sonra dinamik analizle tespit ettiğimiz "string" bilgilerini
kullanarak uygulama içinde bu string'lerin kullanıldığı
fonksiyonları inceleyecek ve hangi fonksiyona odaklanmamız
gerektiğini tahmin etmeye çalışacağız.
• Odaklanmamız gereken fonksiyonu tespit ettikten sonra da
algoritma, veri yapıları ve fonksiyondan çağrılan API
fonksiyonları ve diğer fonksiyonları inceleyerek fonksiyonun
amacını anlamaya çalışacağız.
• Karmaşık adımlarda fonksiyonu pseudo kodlayarak edindiğimiz
algoritma bilgisini kaydedebiliriz.
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
• IDA ismini Interactive Disassembler ifadesinden alır. Bir
çalıştırılabilir dosya IDA tarafından ilk analiz edildiğinde IDA
".idb" uzantılı ayrı bir dosya üzerinde bir veritabanı oluşturur.
• Bu noktadan itibaren disassembly üzerinde yapılan tüm işlemler
(rename etmeler, renklendirmeler, gruplamalar, veri tipi
dönüşümleri, v.d.) bu veritabanı üzerinde yapılır, çalıştırılabilir
dosya ile ilgisi yoktur. Bu nedenle analistler bir zararlı üzerinde
çalıştıklarında sadece IDA veritabanını paylaşarak çalışabilirler,
çalıştırılabilir dosyanın paylaşılması gerekmez.
• Tabi eğer analist isterse assembly kodu üzerinde yaptığı
değişikliği çalıştırılabilir dosyaya yamalayabilir.
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
• DİKKAT: IDA PRO'da "undo" (geri al) imkanı yoktur. Dolayısıyla
belli aralıklarla yedek almakta fayda vardır. Zira veri yapılarını
farklı formatlara dönüştürdüğümüzde (örneğin byte serisini
string gibi yorumlattığımızda, v.b.), fonksiyon, parametre,
değişken isimlerini rename ettiğimizde, veri alanlarını kod gibi
yorumlattığımızda bu işlemleri geri almak vakit kaybettirebilir.
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
IDA arayüzü hakkındaki temel bilgiler
• Işıklandırma (highlighting)
• IDA arayüzünde bir cursor'ü bir kelimenin üzerine
(tıklayarak) konumlandırdığınızda arayüzde aynı kelimenin
geçtiği diğer yerler de highlight edilir.
• Örneğin "call" instruction'ına tıkladığınızda diğer "call"
instruction'ları da highlight edilir. Ya da belli bir register'a
atanan bir değerin nerede tekrar kullanıldığını kolayca
görebilmek için bu register'ın adının üzerine tıklanabilir.
• Bu sayede assembly kodu içinde bizim o anki incelememizle
ilgili bölümler daha kolay ayrıştırılabilir.
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
IDA arayüzü hakkındaki temel bilgiler
• Işıklandırma (highlighting)
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
IDA arayüzü hakkındaki temel bilgiler
• Grafik Görüntü (Graph View) – Metin Görüntü (Text View)
• IDA'nın en önemli faydalarından birisi Graph View imkanıdır.
İki mod arasında "space" tuşu ile geçiş yapılabilir.
SPACE
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
IDA arayüzü hakkındaki temel bilgiler
• Grafik Görüntü (Graph View) – Metin Görüntü (Text View)
• Graph View bize fonksiyonun karmaşıklığı, ana dalları ve
döngüleri hakkında görsel ve dolayısıyla daha kolay
anlaşılabilir bilgi verir.
• IDA döngüleri "kalın mavi" çizgilerle ifade eder.
• Karar dalları (decision branches) eğer koşul doğru (true) ise
"yeşil", değilse "kırmızı" çizgilerle ifade edilir.
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
IDA arayüzü hakkındaki temel bilgiler
• Grafik Görüntü (Graph View) – Metin Görüntü (Text View)
Döngü
if (*EAX = 0) == TRUEif (*EAX = 0) != TRUE
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
Yeniden isimlendirme (renaming)
• Assembly kodunu anlamlandırdıkça fonksiyon isimleri ve
değişkenler üzerine tıklanarak "N" tuşuna basıldığında anlamlı
isimler verebiliriz. Bu işlemi gerçekleştirdiğimizde söz konusu
fonksiyonlara veya değişkenlere referans verilen diğer tüm
noktalarda bizim yeni verdiğimiz isim görülür.
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
Imports alt penceresi (subview)
• PE dosya formatında da değinildiği gibi uygulamanın kullandığı
işletim sistemi API'leri bize uygulamanın amaçları ile ilgili bilgi
verebilir.
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
Strings alt penceresi (subview)
• Kesin bilgi vermese de dinamik analiz ile birleştirildiğinde
string'ler hangi fonksiyonlara odaklanmamız gerektiği hakkında
bize çok yardımcı olabilirler.
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
Strings alt penceresi (subview)
• Bir string'in üzerine çift tıklayarak onun dosya içindeki yerine
ulaşabiliriz.
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
Referanslar
• String'e IDA tarafından verilen ismin üzerine tıkladıktan sonra
"X" tuşuna basılırsa bu string'e referans verilen yerler görülebilir.
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
Referanslar
• Referanslar'dan birisi seçilerek çift tıklandığında assembly kodu
içinde string'e referans verilen alana ulaşılır.
ÖN BİLGİ
IDA PRO HAKKINDA TEMEL BİLGİLER
Geri dönme (ESC tuşu)
• IDA içinde referanslar, fonksiyon isimleri, v.d. üzerine tıklanarak
yapılan geçişlerde geri dönülmek için ESC tuşuna basılabilir.
ESC
1. AŞAMA
İlk olarak basit bir
dinamik analiz ile
başlıyoruz.
1. AŞAMA
Dinamik analiz sırasında gözlemlediğimiz
string'lerden birisini Strings penceresinden
bularak çift tıklayalım.
1. AŞAMA
IDA'nın C string'i olarak algıladığı bu veri
alanına verdiği isme ("aTersineMuhendi")
tıkladıktan sonra "X" tuşuna basarak bu alana
referans veren diğer noktaları izleyelim.
1. AŞAMA
String'imize veri alanına nazaran yukarıdaki [up] object, yani kod tipinde [o]
bir alandan ve tam olarak da [_main] fonksiyonunun 0xBB offset adresinde
bulunan bir instruction'dan referans verildiğini görüyoruz.
Instruction'dan anlaşıldığı kadarıyla bu string'in adresi stack'te bir lokal
değişken alanına yazılıyor.
Şimdi çift tıklayarak bu kod bölümüne geçelim.
1. AŞAMA
[Tersine muhendislik oyunumuza hos geldiniz.]
string'ine referans verilen fonksiyonumuz bu
fonksiyon.
1. AŞAMA Bu kod bölümünde biraz aşağıya doğru
indiğimizde aşamalarla ilgili yönlendirme
metinlerini görebiliyoruz. Bu fonksiyon bizim
ana dallanma noktamız olabilir. Fonksiyon
içinden çağrılan diğer fonksiyonları daha kolay
görebilmek için [call] instruction'larından
birisinin üzerine tıklayarak tüm [call]
instruction'larını highlight edebiliriz.
IDA bazı fonksiyonları [printf] olarak
adlandırmış, bazılarını ise [sub_] ile başlayan
ve yanında da RVA'e benzer bir adres ile
isimlendirmiş.
[printf] fonksiyonuna çift tıkladığımızda bir [stub] kod bölümüne atlıyoruz.
Bu kodda sadece [Import Address Table - IAT]'da bulunan [printf] API
fonksiyon adresine [JMP] instruction'ı bulunuyor. IDA'nın fonksiyon adı
olarak [printf] yazabilmesinin sebebi de bu.
1. AŞAMA
1. AŞAMA
[ASAMA-1] string'i stack'e yazıldıktan hemen sonra [printf] fonksiyonu
çağrılıyor. Bundan sonra da [sub_401B19] fonksiyonu çağrılıyor.
1. AŞAMA
[sub_401B19] fonksiyon adına
tıkladığımızda bu fonksiyonun her
[ASAMA] string'i yazıldıktan sonra
çağrıldığını görebiliyoruz.
1. AŞAMA
[sub_401B19] fonksiyon adına çift
tıkladığımızda bu fonksiyona
ulaşabiliriz. Fonksiyonun içeriğindeki
metinlerden yola çıkarak kesin emin
olmasak da bu fonksiyonun okumayla
ilgili olduğu tahminini yapabiliriz.
1. AŞAMA
Bu fonksiyonun adını tahminimizi yansıtacak biçimde [GIRDI_OKU] olarak
[RENAME] edelim. Eğer hatalıysak daha sonra daha detaylı bir inceleme
yaparak bu adı değiştirme imkanımız var.
Fonksiyon adını değiştirdikten sonra
fonksiyonun görünümü.
1. AŞAMA
Yaptığımız isim değişiklikleri, eklediğimiz yorumlar, kod
değişiklikleri v.d. bilgiler daha önce de belirttiğimiz gibi
IDA'nın oluşturduğu veritabanına yazılır.
1. AŞAMA
1. AŞAMA
ESC tuşu ile [GIRDI_OKU] fonksiyonundan bu
fonksiyonu çağıran ana fonksiyona
döndüğümüzde fonksiyonun çağrıldığı her
noktada isminin [GIRDI_OKU] olarak
güncellendiğini görebiliriz.
1. AŞAMA
[GIRDI_OKU] fonksiyonundan bir sonra çağrılan fonksiyon [sub_401470]
fonksiyonu. [Calling Convention]'lardan bahsederken [EAX] register'ının genellikle
fonksiyonun [RETURN] değerini içerdiğini belirtmiştik.
[GIRDI_OKU] fonksiyonu çağrıldıktan hemen sonra [EAX] register'ının değeri önce
[var_4] lokal değişkenine, oradan tekrar anlamsız bir biçimde [EAX] register'ına
atanıyor. Belli ki kullanılan derleyici bu konuda bir optimizasyon yapmamış veya
yapamamış. Daha sonra [EAX] register'ının değeri [var_18] değişkenine atanıyor
ki; bu değişken de [ESP]'nin işaret ettiği en uç noktada bulunan bir alanda yer
alıyor. X86'ya giriş bölümünde bir fonksiyona [PUSH] instruction'ı ile parametre
vermiştik. Burada ise derleyici yine ilginç bir farklılık göstererek stack'te daha
önceden ayırmış olduğu alanın son bölümünü parametre aktarmak için kullanıyor.
[sub_401470] fonksiyonuna
geçtiğimizde [asdf2009] string'inin
adresini [var_4] lokal değişkenine,
aldığı parametreyi ise [var_8] lokal
değişkenine atadığını görüyoruz.
Bu işlemlerden hemen sonra da
[sub_401492] fonksiyonu
çağrılıyor. Bu fonksiyonun çıktısının
[EAX register değerinin] [0] olup
olmadığı kontrol ediliyor. Eğer [0]
değilse [sub_401C1E] fonksiyonu
çağrılıyor. Sonuç [0] olduğu
durumda da fonksiyon normal bir
biçimde sonlanıyor.
1. AŞAMA
1. AŞAMA
Sonucun [0] olmadığı durumda
çağrılan fonksiyona baktığımızda
son olarak [exit] fonksiyonunun
çağrıldığını görüyoruz.
1. AŞAMA
C ve C++ API fonksiyonlarına MSDN'den veya diğer kaynaklardan göz atabiliriz.
Bu kaynaklarda "exit" fonksiyonu çağrıldığında [process]'in, yani uygulamamızın
kapatıldığını görebiliriz.
1. AŞAMA
Bu fonksiyonu [HATALI_DENEME]
olarak isimlendirelim. Yaptığı iş
sadece [8] statü koduyla
uygulamayı sonlandırmak.
1. AŞAMA
Çıkış yapan fonksiyonu [RENAME]
ettikten sonra kodu biraz daha
rahat okunur hale getirmiş olduk.
1. AŞAMA
İlk adımda çağrılan fonksiyonun
adını da [ASAMA_1] olarak rename
edebiliriz.
1. AŞAMA
[ESC] tuşuyla ana fonksiyona göndüğümüzde çağrılan
fonksiyon adının da güncellendiğini görebiliriz.
1. AŞAMA
Uygulamanın anlaşılabilirliğini
artırmak için [ASAMA_1]
fonksiyonunun aldığı parametreye
isim verelim.
1. AŞAMA
Fonksiyonun aldığı parametreye
[PRM_GIRDI] adını verelim.
1. AŞAMA
[ASAMA_1]
fonksiyonunda asıl
kontrolün yapıldığını
tahmin edebileceğimiz
bir başka fonksiyon daha
[sub_401A02] çağrılıyor.
Bu fonksiyon
çağrıldığında ise [ESP]
register'ının işaret ettiği
alanda [2] adet lokal
değişkenin tutulduğunu
görebiliyoruz.
1. AŞAMA
Bu değişkenlerin de
adlarını biraz daha
anlamlı kılmak üzere
[var_4]'ü [L_ASDF1234],
[var_8]'i [L_GIRDI] olarak
adlandırabiliriz. Eğer
[sub_401A02] fonksiyonu
[2] adet parametre
alıyorsa bunlardan ilki
[L_GIRDI] (yani
kullanıcının girdiği veri),
ikincisi ise [L_ASDF1234],
yani [asdf2009] değeridir.
1. AŞAMA
Tam olarak ne yaptığını
analiz edebilmek için
[sub_401A02] fonksiyon
adına çift tıklayarak bu
fonksiyona geçelim.
1. AŞAMA
Bu fonksiyon önce 1.
parametreyi [arg_0], daha
sonra da [2.] parametreyi
[arg_1] [sub_4019D6]
fonksiyonuna parametre
olarak veriyor ve bu
fonksiyonu çağırıyor.
1. AŞAMA Bu fonksiyona geçtiğimizde
kendisine verilen parametreyi
[EAX] register'ına atadığını
görüyoruz. [EAX] register'ının
geçtiği yerleri daha rahat
görebilmek için bu register'a
tıklayalım.
[var_8] lokal değişkeni
kendisine verilen parametre ile
initialize edilirken [var_4] [0]
değeri ile initialize ediliyor.
Aşağıdaki döngüde [var_4]
sayaç olarak kullanılıyor. [var_8]
lokal değişkeninin işaret ettiği
bellek alananındaki byte ise her
turda [0] ile karşılaştırılıyor.
Son turda [var_4] sayaç değeri
[EAX] register'ına atanarak geri
döndürülüyor.
1. AŞAMA
Yaptığımız analize dayanarak bu
fonksiyonun kendisine parametre
olarak verilen bir C string'inin
uzunluğunu belirlediğini
söyleyebiliriz.
Bu nedenle bu fonksiyona
[MTN_UZUNLUK] adını verebiliriz.
1. AŞAMA
[ESC] tuşu ile geri döndüğümüzde
bu fonksiyonun amacını anlamış
olduğumuzdan, fonksiyona
parametre olarak verilen 1. ve 2.
parametrelerin (yani C
string'lerinin) uzunluklarının
karşılaştırılması neticesinde eğer bu
string'lerin uzunlukları eşitse
sağdaki kod alanlarına geçildiğini
daha rahat görebiliyoruz.
Bunu da kod okunurluğunu
artırabilmek için [JUMP] edilen bu
alanın ismini [UZUNLUK_ESITSE]
ifadesiyle değiştirerek koda
aktarabiliriz.
1. AŞAMA
[sub_401A02] fonksiyonu aldığı 2
parametreyi [var_8] ve [var_C]
lokal değişkenlerine atadıktan
sonra bunlar üzerinde bir inceleme
yapıyor.
Bu parametrelerin ne olduklarını
daha net görebilmek için [ESC]
tuşuna basarak [ASAMA_1]
fonksiyonuna geçebiliriz.
[ASAMA_1] fonksiyonuna geçtiğimizde [1.] parametrenin bizim girdiğimiz değer,
[2.] parametrenin ise [asdf2009] string'i olduğunu görebiliriz.
Uygulama derleyicisinin çağrılan fonksiyonlara parametre aktarma yönteminin
stack'te lokal değişkenler için ayrılan yerleri kullanmak olduğunu daha önceden
değerlendirmiştik.
1. AŞAMA
1. AŞAMA
[sub_401A02] fonksiyonunun
içinde de parametre (argüman)
isimlerini bu bilgilere uygun
biçimde düzenleyebiliriz.
Buna göre [1.] parametreye
[TEST_GIRDISI], [2.] parametreye
[MTN_ASDF2009] adını verebiliriz.
Parametre isimlerini
düzenlediğimizde kodun aşağı
bölümlerinde değeri atanan lokal
değişkenlerin de kullanım
amaçlarını daha iyi anlayabiliriz.
1. AŞAMA
Kodun kalan bölümünde lokal
değişkenler üzerinden işlem
yapıldığı için lokal değişkenleri
[var_8] için [L_TEST_GIRDISI] ve
[var_C] için [L_MTN_ASDF2009]
olarak düzenleyelim.
1. AŞAMA
Fonksiyonun içinde test girdisinin her bir byte'ını kontrol eden bölümde eğer
okunan byte [0] ise Jump edilecek adresi [GIRDI_SONUNA_GELDIK] olarak
adlandırabiliriz.
1. AŞAMA
Girdinin son byte'ına gelinmemesi
durumunda test girdisi ve
[asdf2009] string'lerinin her bir
byte'ının karşılaştırıldığı kod
bölümüne geliyoruz.
Bu karşılaştırmanın sonucunun
eşitlik olmaması durumunda
atlanan kod adresini de
[GIRDI_FARKLI] olarak
isimlendirebiliriz.
1. AŞAMA
Karakterlerin aynı olması halinde test girdisi ve [asdf2009] string'i
içinde işaret edilen byte'ların adresleri [EAX] register'ına atanır.
1. AŞAMA
Test girdisi ve [asdf2009] string'i içinde işaret edilen byte'ların
adresleri [INC] instruction'ı ile [1] artırılır ve döngünün bir sonraki
turuna geçilir.
1. AŞAMA
[sub_401A02] fonksiyonuyla ilgili
analizimiz sonucunda bu
fonksiyonun adını
[MTN_KARSILASTIR] olarak
düzenleyebiliriz. Eğer karşılaştırılan
metin'ler aynı ise bu fonksiyon [0]
değerini, farklı ise [1] değerini
döndürerek sonlanıyor.
1. AŞAMA
[ASAMA_1] fonksiyonuna geri
döndüğümüzde ve çağrılan
fonksiyonun amacının metin
karşılaştırma olduğunu ifade eden
bir fonksiyon adını gördüğümüzde
girilecek olan girdinin [asdf2009]
string'i olması gerektiğini net olarak
söyleyebiliriz.
1. AŞAMA
Bu bilgiyi kullanarak eğer
[MTN_KARSILASTIR] fonksiyonunun
döndürdüğü değer [0] ise atlanan
(JUMP edilen) adresi de
[METINLER_AYNI] şeklinde
isimlendirebiliriz.
1. AŞAMA
1. AŞAMA
Tahminimiz olan [asdf2009]
metnini denediğimizde haklı
olduğumuzu görebiliriz.
NE ÖĞRENDİK
IDA Pro hakkında
• Strings penceresi
• Kod highlighting (ışıklandırma)
• XREF'ler ile kod v.d. bölümlere atlama
• [ESC] tuşu ile geri dönme
• Rename ederek fonksiyon, değişken, v.d. bölümleri
anlamlandırma
• IDB veritabanı dosyaları ve yedekleme ihtiyacı
NE ÖĞRENDİK
Algoritma hakkında
• Döngüler, döngü çıkış koşulları testi ve "i++", yani [INC] kod
örnekleri
• "If" koşulu assembly kod örnekleri ("test eax, eax", "cmp eax,
ebx", v.d.)
X86 ve diğer
• Sistem API'leri ile ilgili bilgi bulma (MSDN, diğer)
• Üzerinde çalıştığımız kodu üreten derleyicinin çağrılan
fonksiyonlara parametre aktarma yolu olarak en başta ayrılan
stack alanını kullanmayı tercih ettiği
OYUN
2. AŞAMA
ÖN BİLGİ
[FOR] DÖNGÜ YAPISI
for (i=0;i<6;i++) {}
[FOR] döngüleri 3 ana bölümden oluşur:
• Sıfırlama [initialization]
• Test
• Sayaç artırma
ÖN BİLGİ
DİZİ [ARRAY] ERİŞİMİ
Dizi erişimleri [FOR] döngüleri içinde sıklıkla rastlanır çünkü dizi
elemanlarının tamamını işlemek gerektiğinde döngüye ihtiyaç
olaraktır.
Genel dizi elemanı erişim şekli aşağıdaki gibidir:
[BASE+COUNT*INCREMENT]
BASE, dizinin başlangıç adresidir. COUNT, dizinin erişilen bileşeninin
indeksidir. INCREMENT, dizi bileşeninin uzunluğudur.
ÖN BİLGİ
DİZİ [ARRAY] ERİŞİMİ
[BASE+COUNT*INCREMENT]
Örneğin; bir INTEGER array
erişiminde bileşen uzunluğu [4] BYTE
olaraktır ve bileşenlere yukarıdaki
gibi erişilecektir.
ÖN BİLGİ
DEĞİŞKEN SAYIDA GİRDİ ALAN [API] FONKSİYONLARI
Bazı API fonksiyonları işlevleri gereği değişken sayıda girdi
alabilirler.
Örneğin; [printf], [scanf], v.b. fonksiyonlar ilk parametreleri olan
[format string]'e uyumlu olarak diğer parametreleri stack'te
bulmayı beklerler.
printf("%d, %d n", i, j);
Ana fonksiyonda farklı aşamaların
çağrıldığı noktaları (printf
fonksiyonu ile) konsola yazılan
mesajlardan yola çıkarak tahmin
edebiliriz. Tahminimiz hatalı olsa
bile değiştirme imkanımız var.
2. AŞAMA
2. AŞAMA
Öncelikle fonksiyonun aldığı
parametreyi daha belirgin hale
getirmek amacıyla [RENAME]
edelim.
2. AŞAMA
Fonksiyonun aldığı parametreyi
kopyaladığı lokal değişkeni de
[RENAME] ederek teste tabi
tutulacak olan verinin izini
sürmeye devam edelim.
2. AŞAMA
[HATALI_DENEME] fonksiyonunun çağrılmasına
neden olabilecek JUMP instruction'ının öncesinde
[var_38] lokal değişkeninin [1] değeri ile
karşılaştırıldığını görüyoruz. Bu karşılaştırmadan
hemen önce de [sub_401974] adlı fonksiyonun
çağrıldığını görüyoruz. Muhtemelen bu
fonksiyonun içinde yapılan bir kontrol bu aşamayı
geçip geçmememize etkide bulunacak.
Dikkat edersek yukarıda [var_38] lokal değişken
alanının adresinin [var_44] lokal değişken alanına
aktarıldığını görebiliriz. Bu da [sub_401974]
fonksiyonu çağrıldığında [var_48] , yani AŞAMA
2'nin girdisiyle birlikte [var_38] lokal değişkeninin
adresinin de fonksiyona parametre olarak
verildiği ihtimalini doğuruyor.
2. AŞAMA
[sub_401974] fonksiyonuna
geçtiğimizde bu fonksiyonun [2]
parametre aldığı tahminimizin
doğru olduğunu IDA'nın yaptığı
[arg_0] ve [arg_4] parametre
çıkarımlarından görebiliriz.
2. AŞAMA
Fare imlecini [L_PRM_GIRDI]
adını verdiğimiz alanın
üzerine getirdiğimizde ve
beklediğimizde bir başka
fonksiyonu çağırdığımızda bu
fonksiyonun alacağı
argümanların sırasını da
yukarıdan aşağıya doğru
görebiliriz.
Buna göre [L_PRM_GIRDI] ->
[arg_0] ise [var_44] -> [arg_4]
olmalıdır.
2. AŞAMA
Edindiğimiz bilgileri yansıtmak için
[ASAMA_2]'nin [var_44]
değişkenini de (fonksiyona verilen
[2.] parametre anlamında)
[F_PRM_2_PTR] olarak
isimlendirebiliriz.
2. AŞAMA
Çağrılan [sub_401974]
fonksiyonuna verilen [2.]
parametre bir pointer. Pointer'ın
işaret ettiği bellek alanı ise
[var_38]'di. Bu alanı da fonksiyon
parametresi ile ilişkilendirmek için
adını [L_PRM_2] olarak
değiştirebiliriz.
2. AŞAMA
Çağrılan [sub_401974]
fonksiyonunun aldığı
parametreleri de anlamlandırmak
için [arg_0] ve [arg_4] adlarını da
değiştirelim.
2. AŞAMA
Fonksiyonun aldığı birinci
parametre bizim ASAMA 2'de
gireceğimiz girdi, ikinci parametre
ise bu fonksiyonun bir şekilde
kullanacağı bir bellek alanının
adresidir. [ASAMA_2]
fonksiyonunda kullandığımız
isimlere benzer olarak birinci
parametreye [PRM_GIRDI], ikinci
parametreye ise [PRM_2_PTR]
adını verebiliriz.
2. AŞAMA [sub_401974] fonksiyonunun
içinden [sscanf] API fonksiyonunun
çağrıldığını görüyoruz. IDA PRO
sistem API fonksiyonlarını tanıdığı
için aldığı parametreleri gösteriyor.
2. AŞAMA
Ancak [sscanf] fonksiyonunu daha iyi tanımak için MSDN veya diğer C ve C++
kaynaklarından faydalanabiliriz.
Buna göre [1.] parametre [sscanf] tarafından okunacak olan C string'ine işaret
ediyor, [2.] parametre okunan C string'indeki verilerin hangi formatta
değerlendirileceğini belirtiyor, diğer parametreler ise format string'de belirtilen
verilerin yazılacağı alanların [pointer]'larından oluşuyor.
2. AŞAMA [sscanf] fonksiyonu çağrıldığında alacağı
parametreleri görebilmek için lokal
değişken isimleri üzerinde mouse
imlecimizi bekletebiliriz. Buna böre [1.]
parametre [var_28], [2.] parametre
[var_24] ve [3.] parametre de [var_20] de
yer almalı. Şimdi bu parametrelere atanan
değerleri inceleyebilir ve isimlerini bizim
için daha anlamlı isimlerle değiştirebiliriz.
2. AŞAMA Fonksiyonun aldığı [1.] parametre olan
[PRM_GIRDI] değeri (ki bu değer aynı zamanda
[ASAMA_2] fonksiyonun aldığı girdi oluyor)
[sscanf]'in ilk parametresi oluyor. Bu
parametreye [CHAR_PTR] adını verelim. [2.]
parametre [%d %d %d %d %d %d] değeri olan
ve 6 adet integer pointer'ına işaret eden bir
string. Bu parametreye [FORMAT_STR_PTR]
adını verelim. [sscanf] fonksiyonuna verilen [3.]
parametre ise içinde bulunduğumuz
[sub_401974] fonksiyonuna [2.] parametre
olarak verilmiş olan bir pointer, bu alana da
[L_PRM_2_PTR_INT] adını verelim. Demek ki
[ASAMA_2] fonksiyonundan parametre olarak
verilen lokal değişken adresi 6 integer değer
adresinden ilkinin adresiymiş.
2. AŞAMA
Windows sistem API dokümantasyonundan [sscanf] fonksiyonunun çıktısının
doldurmayı başardığı parametre sayısı olduğunu anlıyoruz.
Daha önceki incelememizden aldığı format string'de 6 adet [%d], yani integer
değer referansı bulunduğunu söyleyebiliriz. [sscanf]'in çıktısının atandığı [var_4]
lokal değişkeni [5] rakamı ile karşılaştırılıyor.
2. AŞAMA
Eğer fonksiyonun aldığı string içinde [5] veya daha az parametre varsa oyunu
kaybediyoruz.
Kodun okunabilirliğini artırmak için okunan parametre sayısının [5]'ten büyük
olduğunda atlanan adresi [OKUNAN_DEGER_SAYISI_5TENBUYUK] olarak
adlandırabiliriz.
2. AŞAMA
[sub_401974] fonksiyonunu
inceledikten sonra kullanım
amacının 6 adet INTEGER değer
okumak olduğunu söyleyebiliriz.
Bu nedenle fonksiyonun adını
[ALTI_INT_OKU] olarak
değiştirebiliriz.
2. AŞAMA
[ALTI_INT_OKU] fonksiyonu
kendisine [2.] parametre olarak
verilen alandan başlayarak arka
arkaya [6] adet INTEGER değeri
belleğe dolduruyor olmalı.
Bunlardan ilkinin değerinin [1]'den
farklı olması halinde oyun
sonlanıyor. Dolayısıyla [2.]
aşamada girdiğimiz girdinin ilk
rakamı [1] olmalı.
2. AŞAMA
Bu tespitimizi kalıcı hale getirmek
için ilk değerin [1] olduğu durumda
atlanan adrese
[BIRINCI_DEGER_BIR_ISE] adını
verebiliriz.
2. AŞAMA
Birinci değerin [1] olması koşulunu
sağladıktan sonra karşımıza tipik
bir döngü çıkıyor.
Döngünün başında [var_C] lokal
değişkenimize [1] değerinin
atandığı ve bu değişkenin sayaç
olarak kullanıldığını söyleyebiliriz.
2. AŞAMA
[var_C]'nin sayaç olduğunu tahmin
ettiğimize göre bu değişkenin adını
[SAYAC] olarak değiştirebiliriz.
2. AŞAMA
Döngünün çıkış şartı ise sayacın
[5]'ten büyük olması.
Eğer girdiğimiz [6] rakam da
algoritmanın beklediği değerlere
uygunsa [ASAMA_2] fonksiyonu
başarılı bir şekilde sonlanacaktır.
PSEUDO CODE
for (int i=1; i>5; i++) {
...
}
2. AŞAMA
Algoritmanın kritik olan
bölümü biraz karışık. Ancak
yapılan karşılaştırma ilk
parametreden [2] sonraki
integer değerinin [1]
sonraki değerin [2] katı
olup olmadığının
kontrolüdür. Daha sonraki
döngülerde de bu
karşılaştırma bir
kaydırılarak gerçekleştirilir.
[L_PRM_2] okunan
değerlerin atandığı
dizinin ilk elemanının
adresi ise [2.] elemanın
adresi [var_3C]
olacaktır.
2. AŞAMA
PSEUDO CODE
if (arr[0] == 1) {
for (int i=1; i>5; i++) {
if (arr[i+1] != 2*arr[i]) {
HATALI_DENEME();
}
}
} else {
HATALI_DENEME();
}
Karmaşık bir algoritmayı ifade etmenin en iyi yolu pseudo kod ile ortaya
koymaktır.
CEVAP: 1 2 4 8 16 32
2. AŞAMA
Uygulama ile biraz daha
oynadığımızda eğer uygulamaya
bir parametre verirsek bize bir
kullanım mesajı verdiğini
görebiliriz.
Bu mesaja göre uygulamamız bir
dosyayı da girdi olarak kabul
ediyor gibi görünüyor.
2. AŞAMA
Benzeri bir tahmini uygulamanın
string'lerini inceleyerek de
yapabilirdik.
2. AŞAMA
Bu tahminimizi test etmek için [1.] aşamadaki cevabımızı bir dosyaya
yazarak uygulamaya verelim.
Cevaplarımızı yazdıktan sonra bir defa [ENTER]'a basmayı unutmayalım.
Bu şekilde sonraki aşamalarda önceki cevapları tekrar tekrar yazmaktan
kurtulabiliriz.
2. AŞAMA
Uygulamaya parametre olarak
dosya verdiğimizde de
uygulamanın sağlıklı olarak
çalıştığını görebiliriz.
2. AŞAMA
[2.] yanıtımızı da dosyanın içine
yazarak uygulamamızı çalıştıralım.
2. AŞAMA
[2.] aşamadaki tahminimizin de
doğru olduğunu görebiliriz.
NE ÖĞRENDİK
Algoritmalar hakkında
• [FOR] döngüleri assembly kod yapısı
• Sayaç sıfırlama
• Test
• Sayacı artırma
Diziler [Array] hakkında
• Dizi bileşenlerine erişim için adres hesaplama
[TABAN+SAYAC*BOYUT]
API fonksiyonları hakkında
• Değişken parametre alabilen API fonksiyon örnekleri
NE ÖĞRENDİK
Analiz hakkında
• [PSEUDO KOD] yöntemi ile analiz edilen algoritmaları
dokümante etme
OYUN
3. AŞAMA
ÖN BİLGİ
[SWITCH .. CASE] DEYİMİ VE [JUMP TABLE]
Switch .. Case deyimi farklı derleyiciler tarafından farklı şekillerde
assembly'ye dönüştürülür. Bazı derleyiciler iç içe [if .. elseif .. elseif
.. else] şeklinde derlerken bazıları da bir Jump Table dizisi
kullanabilir.
Yukarıdaki kod bloğunda [EAX] register'ının barındırdığı adrese [JUMP] etmeden
önce [var_4] değişkeninin değeri (ki muhtemelen bir indeks değeri) [SHIFT LEFT]
instruction'ı ile [4] ile çarpılıyor. Bu offset'te bir [JUMP TABLE] içinde ilgili adresi
bulmak için kullanılıyor.
3. AŞAMA
3. aşama fonksiyonumuz da bir
önceki aşamada olduğu gibi
[sscanf] fonksiyonunu çağırıyor.
Fakat bu defa format string'imiz
farklı [%d %c %d], yani [INT],
[CHAR], [INT] formatında veriler
okunuyor.
3. AŞAMA
Daha önce de yaptığımız gibi
parametre ve lokal değişken
isimlerini daha anlaşılır kılmak için
[RENAME] edebiliriz.
İlk olarak format string pointer'ının
saklandığı lokal değişkenin ismini
değiştirelim.
3. AŞAMA
Diğer aşamalar gibi alınan
parametrenin ve bu parametrenin
atandığı lokal değişkenlerin
isimlerini değiştirelim.
3. AŞAMA
Bu noktaya kadar karşımızdaki
derleyicinin yaklaşımına alışmış
olmamız lazım. [sscanf]
çağrıldığında Girdi ve format
string'den hemen sonra [3] adet
pointer gelmeli. Bunlar [var_20],
[var_1C] ve [var_18]. Ancak bu
adreslerde pointer'ları saklanan
lokal değişken alanları ise:
• [var_4] [int]
• [var_E] [char]
• [var_8] [int]
3. AŞAMA
Lokal değişken adları olarak
aşağıdakileri kullanabiliriz:
• [var_4] [int] – INT_1
• [var_E] [char] – CHAR
• [var_8] [int] – INT_2
3. AŞAMA
Önceki aşamadan [sscanf]'in
döndürdüğü sonucun okunan veri
sayısı olduğunu biliyoruz.
Uygulamanın sonlanmaması için
en az [3] değer okunması lazım.
3. AŞAMA
Okunan ilk [INTEGER] değer [7]
rakamıyla karşılaştırılıyor ve eğer
[7]den büyükse [JA] [Jump if
Above] instructionı ile oyun
sonlanıyor.
Buna göre ilk değer integer olmalı
ve [7] veya daha küçük bir değer
olmalı.
3. AŞAMA
Doğru yolda ilerlerken ilginç bir olay gerçekleşiyor.
Grafik olarak kod sanki sona gelmiş gibi
görünüyor.
Gerçekte olan ise [ds:off_404264] adresinden
itibaren 1. INTEGER değerin 4 katı [shl eax,2]
offset'te bulunan adrese [JMP] ediyor olmamız.
Bu akış derleyicimizin [SWITCH .. CASE]
statement'ını yorumlama şekli.
3. AŞAMA
[ds:off_404264] adresine atladığımızda toplam 8 adres
barındıran bir JUMP TABLE görüyoruz.
Yani [0-7] arasındaki bir rakamın [4] ile çarpıldığında
bulunabilecek offset'lerde diğer kod bölümlerinin adreslerini
görüyoruz.
3. AŞAMA
Jump table'ımızın ilk değeri olan
olan [loc_40153F] değerine
geçelim [1. INT değerinin "0"
olduğu senaryo için]
3. AŞAMA [EBP – 0xD] adresine [0x62] değeri
yazıldıktan sonra [EBP – 0x8] offset'indeki
değer [0x65] ile karşılaştırılıyor.
Fonksiyonun yukarı kısımlarına bakarsak
daha önceden bu alanın 2. INTEGER
değerin saklanması için kullanıldığını
görebiliriz.
Her nedense IDA PRO bu alana verdiğimiz
ismi burada kullanamamış.
3. AŞAMA
IDA'da bir değeri Decimal
karşılığına dönüştürmek için
Context Menü'yü veya [H] tuşunu
kullanabiliriz.
3. AŞAMA
1. INTEGER değerinin [0] olduğu
durumda 2. INTEGER değeri [101]
ile karşılaştırılıyor.
3. AŞAMA Pek doğru bir isimlendirme olmasa da 2. INTEGER
değerinin [101] olması halinde [JMP] edilen
lokasyonun adını [IKINCI_INT_DEGER_101_ISE] olarak
belirleyebiliriz.
İsim çok uygun olmasa da tüm CASE dallarından hata
alınmadan atlanması gereken kod bölümünü daha
rahat görebiliyoruz.
3. AŞAMA
Son aşamada [EBP + var_D] offset'inde bulunan değer gireceğimiz ikinci
değer olan [CHAR] tipindeki [1] byte'lık değer ile karşılaştırılıyor.
[var_D] lokal değişkeninin değeri ise yukarıda atanmıştı.
3. AŞAMA
Geldiğimiz yola geri dönersek 1.
INT değerinin [0] olduğu durumda
[var_D] lokal değişkenine atanan
değer [0x62] imiş.
3. AŞAMA
2. olarak gireceğimiz verinin tipi
karakter tipinde bir veri olduğu için
[0x62] değerini context menü ile
veya [R] harfine basarak ASCII
karaktere dönüştürelim
3. AŞAMA
Seçtiğimiz dal için geçerli olan veriler
aşağıdaki gibi:
1. INT – [0]
2. CHAR – [b]
3. INT – [101]
3. AŞAMA
Girdi dosyamıza aşağıdaki satırı
ekleyelim (veya siz seçtiğiniz dal için
belirlediğiniz rakamları girebilirsiniz):
0 b 101
3. AŞAMA
NE ÖĞRENDİK
Analiz hakkında
• Switch .. Case deyiminin assembly kodunda Jump Table
kullanılarak uygulanma şekli
• Analiz ettiğimiz kodun kapsamını ihtiyaçlarımıza göre çizebilme,
girdi ve çıktıları dikkate alarak analiz zamanımızı ekonomik
kullanabilme
OYUN
4. AŞAMA
ÖN BİLGİ
RECURSIVE [ÖZYİNELİ] FONKSİYONLAR
Kendini çağıran fonksiyonlara verilen isimdir.
Genellikle aşağıdaki algoritmaların uygulanmasında karşılaşılırlar:
• Böl ve fethet algoritmalarında
• Sıralama (sorting) algoritmalarında
• Dizin, file system arama [tree ve graph traversal] gibi
algoritmalarında
Gözle analizleri çok zor olduğu için analiz sırasında [PSEUDO CODE]
yöntemi ile algoritmanın yazılmasında büyük fayda vardır.
4. AŞAMA
Ana fonksiyonda 4. aşama olarak
tahmin ettiğimiz fonksiyonun içine
girelim.
4. AŞAMA
Önceki fonksiyonlara benzer biçimde
yine okunan veriye bir referans
parametre olarak alınıyor.
Yine [sscanf] fonksiyonu çağrılıyor
ancak bu defa 2 INTEGER değer
okunmaya çalışılıyor.
4. AŞAMA [arg_0] parametresinin adını daha
anlaşılır olması amacıyla
[PRM_GIRDI] olarak değiştirelim.
4. AŞAMA
Alınan parametrenin kopyalandığı
lokal değişkenin, format string'in
adresinin atandığı lokal değişkenin
ve "sscanf" fonksiyonunun okuduğu
değerleri yerleştirdiği lokal
değişkenlerin adlarını önceki
çalışmamızda olduğu biçimde
değiştirelim.
4. AŞAMA
Kodun bu bölümünü okuyarak okunan parametre sayısının [2] olması gerektiğini,
okunan 2. INTEGER'ın [ 1 < INT_2 < 5 ] aralığında olması gerektiğini söyleyebiliriz.
4. AŞAMA
Derleyicimizin çağrılan fonksiyonlara parametre aktarmak için stackte ayrılmış olan bölümün
son kısımlarını kullanmayı tercih ettiğini ve Visual Studio gibi PUSH instruction'ları ile
parametre aktarımı gerçekleştirmediğini daha önce konuşmuştuk.
Bu bölümde daha önce format string pointer'ını ve fonksiyona verilen parametrenin bir
kopyasını saklamak için kullanılan lokal değişken alanlarının farklı amaçlarla kullanıldığını
görebiliyoruz.
Bu alanlar sırasıyla [INT_2] ve [5] değerlerini barındırarak çağrılan [sub_401603]
fonksiyonuna parametre aktarmak amacıyla kullanılıyor.
Bu bilgileri not edebilmek için IDA'nın comment özelliğini [;] kullanabiliriz.
4. AŞAMA
Bu bölümde algoritmamızın başarılı sonuçlanabilmesi için çağrılan fonksiyonun
döndürdüğü değer ile [INT_1] değerinin aynı olması gerektiğini görebiliriz.
4. AŞAMA
Bu aşamayı çözebilmemiz için çağrılan [sub_401603] fonksiyonunda uygulanan
algoritmayı çözmemiz lazım.
4. AŞAMA [sub_401603] fonksiyonu tahmin
ettiğimiz gibi 2 parametre alıyor. 1.
parametrenin [0] ve [1] olduğu
durumlarda uygulama farklı dallardan
ilerliyor.
Bu biraz garip çünkü daha önce 1.
parametrenin değerinin [5] olacağını
görmüştük.
4. AŞAMA Fonksiyonun aldığı parametrelerin
daha iyi anlaşılabilmesi için parametre
isimlerini [PRM_1_ILK_DEGER_5] ve
[PRM_2_INT_2] olarak değiştirelim
4. AŞAMA Fonksiyon dallarını daha iyi
anlamlandırmak için 1. parametre'nin
[0]'dan büyük olması halinde atlanan
kod adresine
[ILK_PARAMETRE_1_VE_DAHABUYUK]
diyelim.
4. AŞAMA
En sağdaki dal ise 1. parametrenin [2]
veya daha üstü olduğu durumda
işletiliyor. Bu nedenle bu dalın
adresine de
[ILK_PARAMETRE_2_VE_DAHABUYUK]
adını verebiliriz.
4. AŞAMA
1. parametre'nin [0] olması halinde fonksiyon [0] değerini
döndürüyor, [1] olması halinde ise [2. parametre'nin
değeri]ni döndürüyor. (IDA PRO'da en alttaki kutuda
[var_8] lokal değişkeninin değerinin [EAX]'e atandığını
görebilirsiniz.)
Bu tespitlerimizi not etmek için de [;] comment işaretini
kullanarak ilgili kodların yanına kayıt düşebiliriz.
4. AŞAMA
1. parametre'nin "2" veya daha büyük olduğu
koşulda devreye giren kod bölümünde [CALL] edilen
fonksiyon adlarına baktığımızda fonksiyonun
kendisini çağırdığını görüyoruz.
Bu durum fonksiyona ilk parametre olarak "5"
değerini sabit değer olarak vermemize rağmen
neden [0] ve [1] değerleri ile ilgili kod bölümlerinin
var olduğuna dair bize biraz ışık tutuyor. Belki de 1.
parametre fonksiyonun sonraki recursive
çağrılmaları öncesinde daha farklı 1. parametre
değerleri alıyordur.
4. AŞAMA
Fonksiyonumuzun tam olarak ne yaptığını henüz çözmüş değiliz, ancak onu
şimdilik de olsa [RECURSIVE_FUNC] olarak değiştirebiliriz. Bu isim biraz da olsa
kodun okunabilirliğini artıracaktır.
4. AŞAMA [RECURSIVE_FUNC] fonksiyonunun
stack frame'ine göz attığımızda stack'in
uç noktasında [var_10] ve [var_C]
değişkenleri bulunduğunu görüyoruz.
4. AŞAMA
Fonksiyonun 1. parametresine [M], 2. parametresine [N] dersek 1. recursive
çağrının [M-1] ve [N] parametreleri ile yapıldığını görebiliriz.
4. AŞAMA
[EBX] register'ı önce 1. recursive çağrının sonucunu saklamak için kullanılıyor.
Daha sonra da [N] rakamı, yani 2. parametre [EBX] register'ına ekleniyor.
Kodun aşağı bölümlerinde 2. recursive çağrının sonucunun da [EBX]
register'ına eklendiğini ve toplam rakamın da [var_8] lokal değişkenine
atandıktan sonra fonksiyon return değeri olarak döndürüldüğünü görebiliriz.
4. AŞAMA 2. çağrının 1. parametresi olarak da
[M-2] rakamının kullanıldığını
görebiliriz.
4. AŞAMA
2. recursive çağrının 2. parametresi olarak da [N] rakamı kullanılıyor. Yani 2.
çağrı F(M-2,N) şeklinde gerçekleşiyor.
4. AŞAMA
2. recursive çağrının sonucu daha önce [EBX]
register'ında saklanan veriye ekleniyor ve bu
değer fonksiyonun çıktısı olarak
döndürülüyor. Yani fonksiyonumuzu
aşağıdaki gibi tanımlayabiliriz:
F(M,N) = F(M-1,N)+F(M-2,N)+N
KURALLAR
Bu aşamayı geçmek için tespit ettiğimiz kuralları sıralarsak:
• M: Birinci parametre
• N: İkinci parametre
• M=0 ise 0 döndür [F(0,N)=0]
• M=1 ise N'i döndür [F(1,N)=N]
• M=5'tir
• N 2'den küçük ve 4'ten büyük olamaz
• FORMÜL => F(M,N) = F(M-1,N) + F(M-2,N) + N
4. AŞAMA
İlk parametre "5" olmak zorunda olduğuna göre fonksiyonun F(5,2)
parametreleri ile vereceği yanıtı hesaplamaya çalışalım.
• F(5,2)=F(4,2)+F(3,2)+2
• F(5,2)=[F(3,2)+F(2,2)+2]+[F(2,2)+2+2]+2
• F(5,2)=[[F(2,2)+2+2]+[2+0+2]+2]+[[2+0+2]+2+2]+2
• F(5,2)=[[[2+0+2]+2+2]+[2+0+2]+2]+[[2+0+2]+2+2]+2
• F(5,2)=24
4. AŞAMA
4. AŞAMA
[RECURSIVE_FUNC] fonksiyonunun
çağrıldığı [ASAMA_4] fonksiyonumuza
dönersek recursive fonksiyonumuzdan
dönen sonucun [ASAMA_4]
fonksiyonuna verilen 1. INTEGER
rakam ile aynı olması gerektiğini
görebiliriz. 2. INTEGER değeri de
recursive fonksiyona parametre olarak
verilen 2. parametre değeri olacak.
4. AŞAMA
Yaptığımız hesaplamalara göre 4. aşama için [24 2] girdileri başarılı olmalı.
4. AŞAMA
NE ÖĞRENDİK
Recursive fonksiyonlar ve analizleri hakkında
• Recursive fonksiyonların gözle analizi çok zor olduğundan
[PSEUDO CODE] kullanılarak analiz edilmelerinde fayda vardır.
• Girdilerin üreteceği çıktıları tahmin etmeye çalışırken fonksiyon
çağrılarını daha fazla recursive çağrı yapılmayan durumlara
kadar indirgemek gereklidir.
OYUN
5. AŞAMA
ÖN BİLGİ
STRING/VERİ DECODE ETME
• Kodlama (encoding), kriptolama (encryption) gibi algoritmalarda
kullanılan veri dönüştürme yöntemidir.
• Okunan veri dönüştürüldükten sonra yine aynı adrese veya farklı
bir adrese kopyalanarak yazılır.
• Tek byte veya çoklu byte [WORD, DWORD] üzerinde işlem
yapılabilir.
• String decode etme durumlarında veri IDA'da [HEX] olarak
görüntüleneceğinden veri tipini değiştirme veya bir ASCII tablo
kullanma ihtiyacı olabilir.
• Genel algoritma:
• Bellekten oku
• Dönüştür
• Belleğe yaz
ÖN BİLGİ
BIT MASK İLE MASKELEME
• Belli bir verinin belirli [bit]'lerini sabit bırakmak ve diğerlerini
[0]'lamak için kullanılan yöntemdir.
• Logical [AND] operatörü ve bir [Bit Mask] kullanılarak
gerçekleştirilir.
Yukarıdaki işlemde [EAX]
register'ının son nibble'ı (4 bit'i) hariç
tüm bit'leri sıfırlanacaktır.
5. AŞAMA
Ana fonksiyonda 5. aşama olarak
tahmin ettiğimiz fonksiyonun içine
girelim.
5. AŞAMA
IDA'nın da yardımıyla [ASAMA_5]
fonksiyonunun da tek bir parametre
aldığını görebiliyoruz.
5. AŞAMA
Okunabilirliği biraz daha artırabilmek
için alınan parametrenin adını
[PRM_GIRDI] olarak değiştirelim.
5. AŞAMA
Daha önce analiz ettiğimiz ve
[MTN_UZUNLUK] olarak
isimlendirdiğimiz fonksiyon tekrar
karşımıza çıktı.
Stack frame'in sonunda [var_38]
alanı var ve fonksiyon bu adresteki
veriyi parametre olarak alıyor olmalı.
5. AŞAMA
[var_38] alanına [PRM_GIRDI]
parametresi kopyalandığı için
okunablirliği artırmak adına bu alanı
[L_PRM_GIRDI] olarak değiştirelim.
5. AŞAMA
Daha önceki analizimizden
[MTN_UZUNLUK] fonksiyonunun
kendisine parametre olarak verilen
metnin içindeki karakter sayısını
döndürdüğünü biliyoruz.
Döndürülen değer [6] rakamı ile
karşılaştırılıyor ve eğer [6]'dan farklı
ise uygulamayı sonlandırmak üzere
[HATALI_DENEME] fonksiyonu
çağrılıyor.
5. AŞAMA
Kodun devamına baktığımızda bir
döngü görüyoruz ve [var_C]
değişkeni sayaç olarak kullanılıyor.
Döngünün başında [0] olarak
belirlenen sayaç değeri her döngü de
[5]'ten büyük mü diye kontrol
ediliyor.
5. AŞAMA
Anlaşılabilirliği biraz daha artırmak
için [var_C] değişkeninin adını
[SAYAC] olarak değiştirebiliriz.
5. AŞAMA
[SAYAC] sağ alttaki kutuda
bulunan [INC] instruction'ı ile
[1] artırılıyor.
for (i = 0; i < 6; i++){
...
}
5. AŞAMA
Comment'lerle açıklanmış olan bu bölümde ilk 3 instruction sonucunda [var_28]
alandan [SAYAC] offset'in adresi [EDX]e atanıyor. Bu adres biraz sonra yapılacak
işlemin sonucunun yazılacağı adres olarak kullanılacak.
4,5,6. instruction'larda fonksiyonun girdi alanının (PRM_GIRDI) [SAYAC]
offset'indeki BYTE değeri [0xF] değeri ile binary [AND] işlemine tabi tutuluyor. Yani
bu byte'ın sadece düşük değerli yarısı [4 bit] [EAX] register'ında kalıyor.
5. AŞAMA
Her bir döngüde okunan girdi karakterinin son 4 bit'i
[0x403000] adresinden itibaren offset değeri olarak
kullanılıyor ve bu offset'teki byte değeri [EAX]'e atanıyor.
5. AŞAMA
[0x403000] adresine göz attığımızda burada bir dizi karakterin
veri olarak bulunduğunu görüyoruz.
5. AŞAMA
[0x403000] adresinden başlayan alandaki verilere göz
attığımızda bu adresten itibaren belli bir offset'ten atanan [1]
byte'lık verinin bir karakter olduğunu tahmin edebiliriz.
5. AŞAMA
Atanmış olan karakter [var_28] adresinden itibaren her
döngüde [1] byte ilerleyerek yazılıyor.
5. AŞAMA
[var_28] adresinin kullanım amacıyla ilgili tespitimizi de yorum
satırı olarak kaydedebiliriz.
5. AŞAMA
Toplam 6 karakter [var_28] adresinden başlayarak yazıldıktan sonra döngü
sonlanıyor ve [var_22] adresine [0] yazılmış. [0] yani [NULL] byte'ı C string'leri
için string'in sonunu belirtiyor. Bir diğer deyişle [var_28] adresinden başlayan
veri sonuna eklenen null byte ile bir C string'ine çevrilmiş.
5. AŞAMA
[var_34] değişkenine [btrisk] string'inin adresi yazıldıktan sonra [var_28]
alanının adresi [EAX] register'ına atanıyor.
5. AŞAMA
Daha önceden analiz ettiğimiz [MTN_KARSILASTIR] fonksiyonu çağrıldığın da
stack frame'in en üzerinde [var_38] ve [var_34] alanlarının bulunduğunu
görebiliriz. Tabi daha önce [var_38]'i [L_PRM_GIRDI] adıyla değiştirmiştik,
ancak bu alan burada farklı bir veriyi tutmak üzere kullanılacak.
5. AŞAMA
[MTN_KARSILASTIR] fonksiyonumuz daha önceki analizimizden de bildiğimiz
üzere kendisine verilen 2 string'i birbiri ile karşılaştırıyor.
5. AŞAMA
Eğer [var_38]'de (şimdiki adı ile L_PRM_GIRDI) bulunan string [btrisk]
string'inden farklı ise [MTN_KARSILASTIR] fonksiyonunun [1] döndürmesi
gerekiyor (daha önceki analizimizde bunu tespit etmiştik).
5. AŞAMA
2 string birbirinden farklı ise uygulamamız sonlanıyor. Eğer üretilen string
[btrisk] string'iyle aynıysa bu aşamayı başarı ile tamamlamış olmamız
gerekiyor.
5. AŞAMA
KURALLAR
Bu aşamayı geçmek için tespit ettiğimiz kuralları sıralarsak:
• 6 karakterli bir string girmeliyiz
• Her bir karakterin byte değerinin son nibble'ını [son 4
bit'ini] 0x403000 adresinden itibaren offset olarak
kullanarak bir karakter dizisi elde etmeliyiz
• Elde ettiğimiz yeni karakter dizisi "btrisk" olmalı
5. AŞAMA
Sondan başa doğru ilerlersek [btrisk] dizisini oluşturmak için
önce doğru indeks'leri (offset'leri) bulmalıyız.
Sıra:1 Indeks:13 [0xD]
Sıra:2 Indeks:11 [0xB]
Sıra:3 Indeks:6 [0x6]
Sıra:4 Indeks:4 [0x4]
Sıra:5 Indeks:7 [0x7]
Sıra:6 Indeks:14 [0xE]
0xD
0xB
0x6
0x4
0x7
0xE
5. AŞAMA
[0xDB647E] nibble'larını üretecek karakterleri bulmak için bir
ASCII tablosundan faydalanabiliriz.
5. AŞAMA
İstediğimiz sonucu verecek örnek bir karakter dizisi olarak
[MKVT7N]'yi deneyebiliriz.
5. AŞAMA
NE ÖĞRENDİK
Veri encode etme analizi hakkında
• ASCII tablosundan faydalanma
• Bit maskeleme
• Veri dönüştürme (encode) etme örneği
BTRisk X86 Tersine Mühendislik Eğitim Sunumu - Bölüm-2

More Related Content

What's hot

Kurumsal Ağlarda Log İnceleme Yöntemiyle Saldırı Analizi
Kurumsal Ağlarda Log İnceleme Yöntemiyle Saldırı AnaliziKurumsal Ağlarda Log İnceleme Yöntemiyle Saldırı Analizi
Kurumsal Ağlarda Log İnceleme Yöntemiyle Saldırı AnaliziBGA Cyber Security
 
Web Servislerine Yönelik Sızma Testleri
Web Servislerine Yönelik Sızma TestleriWeb Servislerine Yönelik Sızma Testleri
Web Servislerine Yönelik Sızma TestleriBGA Cyber Security
 
Sizma testine giris - Fuat Ulugay
Sizma testine giris - Fuat UlugaySizma testine giris - Fuat Ulugay
Sizma testine giris - Fuat UlugayFuat Ulugay, CISSP
 
GÜVENLİK SİSTEMLERİNİ ATLATMA
GÜVENLİK SİSTEMLERİNİ ATLATMAGÜVENLİK SİSTEMLERİNİ ATLATMA
GÜVENLİK SİSTEMLERİNİ ATLATMABGA Cyber Security
 
Beyaz Şapkalı Hacker CEH Eğitimi - Bölüm 1, 2, 3
Beyaz Şapkalı Hacker CEH Eğitimi - Bölüm 1, 2, 3Beyaz Şapkalı Hacker CEH Eğitimi - Bölüm 1, 2, 3
Beyaz Şapkalı Hacker CEH Eğitimi - Bölüm 1, 2, 3BGA Cyber Security
 
SIZMA TESTLERİNDE BİLGİ TOPLAMA
SIZMA TESTLERİNDE BİLGİ TOPLAMASIZMA TESTLERİNDE BİLGİ TOPLAMA
SIZMA TESTLERİNDE BİLGİ TOPLAMABGA Cyber Security
 
WEB ve MOBİL SIZMA TESTLERİ
WEB ve MOBİL SIZMA TESTLERİ WEB ve MOBİL SIZMA TESTLERİ
WEB ve MOBİL SIZMA TESTLERİ BGA Cyber Security
 
Yazılım Güvenliği Temelleri
Yazılım Güvenliği TemelleriYazılım Güvenliği Temelleri
Yazılım Güvenliği TemelleriBGA Cyber Security
 
Nessus Kullanım Kitapçığı
Nessus Kullanım KitapçığıNessus Kullanım Kitapçığı
Nessus Kullanım KitapçığıBGA Cyber Security
 
Beyaz Şapkalı Hacker (CEH) Lab Kitabı
Beyaz Şapkalı Hacker (CEH) Lab KitabıBeyaz Şapkalı Hacker (CEH) Lab Kitabı
Beyaz Şapkalı Hacker (CEH) Lab KitabıBGA Cyber Security
 
Log Yönetimi ve Saldırı Analizi Eğitimi - 2
Log Yönetimi ve Saldırı Analizi Eğitimi - 2Log Yönetimi ve Saldırı Analizi Eğitimi - 2
Log Yönetimi ve Saldırı Analizi Eğitimi - 2BGA Cyber Security
 

What's hot (20)

Uygulamali Sizma Testi (Pentest) Egitimi Sunumu - 3
Uygulamali Sizma Testi (Pentest) Egitimi Sunumu - 3Uygulamali Sizma Testi (Pentest) Egitimi Sunumu - 3
Uygulamali Sizma Testi (Pentest) Egitimi Sunumu - 3
 
Sizma testi bilgi toplama
Sizma testi bilgi toplamaSizma testi bilgi toplama
Sizma testi bilgi toplama
 
Metasploit El Kitabı
Metasploit El KitabıMetasploit El Kitabı
Metasploit El Kitabı
 
Kurumsal Ağlarda Log İnceleme Yöntemiyle Saldırı Analizi
Kurumsal Ağlarda Log İnceleme Yöntemiyle Saldırı AnaliziKurumsal Ağlarda Log İnceleme Yöntemiyle Saldırı Analizi
Kurumsal Ağlarda Log İnceleme Yöntemiyle Saldırı Analizi
 
Web Servislerine Yönelik Sızma Testleri
Web Servislerine Yönelik Sızma TestleriWeb Servislerine Yönelik Sızma Testleri
Web Servislerine Yönelik Sızma Testleri
 
Sizma testine giris - Fuat Ulugay
Sizma testine giris - Fuat UlugaySizma testine giris - Fuat Ulugay
Sizma testine giris - Fuat Ulugay
 
GÜVENLİK SİSTEMLERİNİ ATLATMA
GÜVENLİK SİSTEMLERİNİ ATLATMAGÜVENLİK SİSTEMLERİNİ ATLATMA
GÜVENLİK SİSTEMLERİNİ ATLATMA
 
Beyaz Şapkalı Hacker CEH Eğitimi - Bölüm 1, 2, 3
Beyaz Şapkalı Hacker CEH Eğitimi - Bölüm 1, 2, 3Beyaz Şapkalı Hacker CEH Eğitimi - Bölüm 1, 2, 3
Beyaz Şapkalı Hacker CEH Eğitimi - Bölüm 1, 2, 3
 
Uygulamali Sizma Testi (Pentest) Egitimi Sunumu - 2
Uygulamali Sizma Testi (Pentest) Egitimi Sunumu - 2Uygulamali Sizma Testi (Pentest) Egitimi Sunumu - 2
Uygulamali Sizma Testi (Pentest) Egitimi Sunumu - 2
 
SIZMA TESTLERİNDE BİLGİ TOPLAMA
SIZMA TESTLERİNDE BİLGİ TOPLAMASIZMA TESTLERİNDE BİLGİ TOPLAMA
SIZMA TESTLERİNDE BİLGİ TOPLAMA
 
WEB ve MOBİL SIZMA TESTLERİ
WEB ve MOBİL SIZMA TESTLERİ WEB ve MOBİL SIZMA TESTLERİ
WEB ve MOBİL SIZMA TESTLERİ
 
Yazılım Güvenliği Temelleri
Yazılım Güvenliği TemelleriYazılım Güvenliği Temelleri
Yazılım Güvenliği Temelleri
 
Hacking'in Mavi Tarafı -2
Hacking'in Mavi Tarafı -2Hacking'in Mavi Tarafı -2
Hacking'in Mavi Tarafı -2
 
Nessus Kullanım Kitapçığı
Nessus Kullanım KitapçığıNessus Kullanım Kitapçığı
Nessus Kullanım Kitapçığı
 
Hacking'in Mavi Tarafı -1
Hacking'in Mavi Tarafı  -1Hacking'in Mavi Tarafı  -1
Hacking'in Mavi Tarafı -1
 
Kesif ve Zafiyet Tarama
Kesif ve Zafiyet TaramaKesif ve Zafiyet Tarama
Kesif ve Zafiyet Tarama
 
Beyaz Şapkalı Hacker (CEH) Lab Kitabı
Beyaz Şapkalı Hacker (CEH) Lab KitabıBeyaz Şapkalı Hacker (CEH) Lab Kitabı
Beyaz Şapkalı Hacker (CEH) Lab Kitabı
 
BTRİSK Web Uygulama Güvenliği Denetimi Eğitim Sunumu
BTRİSK Web Uygulama Güvenliği Denetimi Eğitim SunumuBTRİSK Web Uygulama Güvenliği Denetimi Eğitim Sunumu
BTRİSK Web Uygulama Güvenliği Denetimi Eğitim Sunumu
 
Kali linux
Kali linuxKali linux
Kali linux
 
Log Yönetimi ve Saldırı Analizi Eğitimi - 2
Log Yönetimi ve Saldırı Analizi Eğitimi - 2Log Yönetimi ve Saldırı Analizi Eğitimi - 2
Log Yönetimi ve Saldırı Analizi Eğitimi - 2
 

Similar to BTRisk X86 Tersine Mühendislik Eğitim Sunumu - Bölüm-2

İleri Seviye T-SQL Programlama - Chapter 14
İleri Seviye T-SQL Programlama - Chapter 14İleri Seviye T-SQL Programlama - Chapter 14
İleri Seviye T-SQL Programlama - Chapter 14Cihan Özhan
 
Linux Komut Satırı
Linux Komut Satırı Linux Komut Satırı
Linux Komut Satırı Kemal Demirez
 
İleri Seviye Programlama 2
İleri Seviye Programlama 2İleri Seviye Programlama 2
İleri Seviye Programlama 2Caner Bovatekin
 
ARM Mimarisinde Exploit Geliştirme
ARM Mimarisinde Exploit GeliştirmeARM Mimarisinde Exploit Geliştirme
ARM Mimarisinde Exploit GeliştirmeSignalSEC Ltd.
 
Fonksiyonlarpowerpoint
FonksiyonlarpowerpointFonksiyonlarpowerpoint
FonksiyonlarpowerpointOsman Sabır
 
İleri Seviye T-SQL Programlama - Chapter 02
İleri Seviye T-SQL Programlama - Chapter 02İleri Seviye T-SQL Programlama - Chapter 02
İleri Seviye T-SQL Programlama - Chapter 02Cihan Özhan
 
kullanıcı tanımlı fonksiyonlar
kullanıcı tanımlı fonksiyonlarkullanıcı tanımlı fonksiyonlar
kullanıcı tanımlı fonksiyonlaroktaygokgol
 
Ileri seviye javascript by Azer Koculu
Ileri seviye javascript by Azer KoculuIleri seviye javascript by Azer Koculu
Ileri seviye javascript by Azer Koculumustafa sarac
 
Bas5e bol 03_turkce_tamami
Bas5e bol 03_turkce_tamamiBas5e bol 03_turkce_tamami
Bas5e bol 03_turkce_tamamimuratgulenc
 
Tersine Mühendislik 101
Tersine Mühendislik 101Tersine Mühendislik 101
Tersine Mühendislik 101Fatih Erdoğan
 
Kurumsal Yazılım Geliştirme ve Visual Studio 2008
Kurumsal Yazılım Geliştirme ve Visual Studio 2008Kurumsal Yazılım Geliştirme ve Visual Studio 2008
Kurumsal Yazılım Geliştirme ve Visual Studio 2008mtcakmak
 
İleri Seviye T-SQL Programlama - Chapter 21
İleri Seviye T-SQL Programlama - Chapter 21İleri Seviye T-SQL Programlama - Chapter 21
İleri Seviye T-SQL Programlama - Chapter 21Cihan Özhan
 
RPC Zafiyetlerinin Keşfi
RPC Zafiyetlerinin KeşfiRPC Zafiyetlerinin Keşfi
RPC Zafiyetlerinin KeşfiSignalSEC Ltd.
 

Similar to BTRisk X86 Tersine Mühendislik Eğitim Sunumu - Bölüm-2 (20)

BTRisk iOS Mobil Uygulama Denetimi Eğitimi
BTRisk iOS Mobil Uygulama Denetimi EğitimiBTRisk iOS Mobil Uygulama Denetimi Eğitimi
BTRisk iOS Mobil Uygulama Denetimi Eğitimi
 
İleri Seviye T-SQL Programlama - Chapter 14
İleri Seviye T-SQL Programlama - Chapter 14İleri Seviye T-SQL Programlama - Chapter 14
İleri Seviye T-SQL Programlama - Chapter 14
 
Sunu algo02
Sunu algo02Sunu algo02
Sunu algo02
 
Linux Komut Satırı
Linux Komut Satırı Linux Komut Satırı
Linux Komut Satırı
 
İleri Seviye Programlama 2
İleri Seviye Programlama 2İleri Seviye Programlama 2
İleri Seviye Programlama 2
 
ARM Mimarisinde Exploit Geliştirme
ARM Mimarisinde Exploit GeliştirmeARM Mimarisinde Exploit Geliştirme
ARM Mimarisinde Exploit Geliştirme
 
Fonksiyonlarpowerpoint
FonksiyonlarpowerpointFonksiyonlarpowerpoint
Fonksiyonlarpowerpoint
 
Typescript
TypescriptTypescript
Typescript
 
Assembly for Hackers
Assembly for HackersAssembly for Hackers
Assembly for Hackers
 
İleri Seviye T-SQL Programlama - Chapter 02
İleri Seviye T-SQL Programlama - Chapter 02İleri Seviye T-SQL Programlama - Chapter 02
İleri Seviye T-SQL Programlama - Chapter 02
 
kullanıcı tanımlı fonksiyonlar
kullanıcı tanımlı fonksiyonlarkullanıcı tanımlı fonksiyonlar
kullanıcı tanımlı fonksiyonlar
 
Ileri seviye javascript by Azer Koculu
Ileri seviye javascript by Azer KoculuIleri seviye javascript by Azer Koculu
Ileri seviye javascript by Azer Koculu
 
Visual Studio Developer Tools
Visual Studio Developer ToolsVisual Studio Developer Tools
Visual Studio Developer Tools
 
Bas5e bol 03_turkce_tamami
Bas5e bol 03_turkce_tamamiBas5e bol 03_turkce_tamami
Bas5e bol 03_turkce_tamami
 
Tersine Mühendislik 101
Tersine Mühendislik 101Tersine Mühendislik 101
Tersine Mühendislik 101
 
Kurumsal Yazılım Geliştirme ve Visual Studio 2008
Kurumsal Yazılım Geliştirme ve Visual Studio 2008Kurumsal Yazılım Geliştirme ve Visual Studio 2008
Kurumsal Yazılım Geliştirme ve Visual Studio 2008
 
İleri Seviye T-SQL Programlama - Chapter 21
İleri Seviye T-SQL Programlama - Chapter 21İleri Seviye T-SQL Programlama - Chapter 21
İleri Seviye T-SQL Programlama - Chapter 21
 
RPC Zafiyetlerinin Keşfi
RPC Zafiyetlerinin KeşfiRPC Zafiyetlerinin Keşfi
RPC Zafiyetlerinin Keşfi
 
Temel ABAP eğitim kılavuzu
Temel ABAP eğitim kılavuzuTemel ABAP eğitim kılavuzu
Temel ABAP eğitim kılavuzu
 
Pivot Unpivot
Pivot UnpivotPivot Unpivot
Pivot Unpivot
 

More from BTRisk Bilgi Güvenliği ve BT Yönetişim Hizmetleri

More from BTRisk Bilgi Güvenliği ve BT Yönetişim Hizmetleri (16)

BTRisk - Siber Olay Tespit ve Mudahale Egitimi
BTRisk - Siber Olay Tespit ve Mudahale EgitimiBTRisk - Siber Olay Tespit ve Mudahale Egitimi
BTRisk - Siber Olay Tespit ve Mudahale Egitimi
 
Yazıcı Güvenliği
Yazıcı GüvenliğiYazıcı Güvenliği
Yazıcı Güvenliği
 
BTRISK ISO27001 UYGULAMA EGITIMI SUNUMU
BTRISK ISO27001 UYGULAMA EGITIMI SUNUMUBTRISK ISO27001 UYGULAMA EGITIMI SUNUMU
BTRISK ISO27001 UYGULAMA EGITIMI SUNUMU
 
Kali Linux Hakkında Herşey
Kali Linux Hakkında HerşeyKali Linux Hakkında Herşey
Kali Linux Hakkında Herşey
 
Unix Denetim Dokümanı
Unix Denetim DokümanıUnix Denetim Dokümanı
Unix Denetim Dokümanı
 
BTRisk Android Mobil Uygulama Denetimi Eğitimi
BTRisk Android Mobil Uygulama Denetimi EğitimiBTRisk Android Mobil Uygulama Denetimi Eğitimi
BTRisk Android Mobil Uygulama Denetimi Eğitimi
 
BTRisk Yazılım Güvenliği Yönetimi Eğitimi
BTRisk Yazılım Güvenliği Yönetimi EğitimiBTRisk Yazılım Güvenliği Yönetimi Eğitimi
BTRisk Yazılım Güvenliği Yönetimi Eğitimi
 
BTRisk Android Uygulamalara Malware Yerleştirme Sunumu
BTRisk Android Uygulamalara Malware Yerleştirme SunumuBTRisk Android Uygulamalara Malware Yerleştirme Sunumu
BTRisk Android Uygulamalara Malware Yerleştirme Sunumu
 
BTRisk ISO 27001:2013 Bilgilendirme ve İç Denetim Eğitimi Sunumu
BTRisk ISO 27001:2013 Bilgilendirme ve İç Denetim Eğitimi SunumuBTRisk ISO 27001:2013 Bilgilendirme ve İç Denetim Eğitimi Sunumu
BTRisk ISO 27001:2013 Bilgilendirme ve İç Denetim Eğitimi Sunumu
 
Bilgi Güvenliği Farkındalık Eğitimi Sunumu
Bilgi Güvenliği Farkındalık Eğitimi SunumuBilgi Güvenliği Farkındalık Eğitimi Sunumu
Bilgi Güvenliği Farkındalık Eğitimi Sunumu
 
BTRisk Adli Bilişim Eğitimi Sunumu
BTRisk Adli Bilişim Eğitimi SunumuBTRisk Adli Bilişim Eğitimi Sunumu
BTRisk Adli Bilişim Eğitimi Sunumu
 
BTRİSK Web Uygulama Güvenliği Denetimi Eğitimi
BTRİSK Web Uygulama Güvenliği Denetimi EğitimiBTRİSK Web Uygulama Güvenliği Denetimi Eğitimi
BTRİSK Web Uygulama Güvenliği Denetimi Eğitimi
 
BTRWATCH ISO27001 Yazılımı
BTRWATCH ISO27001 YazılımıBTRWATCH ISO27001 Yazılımı
BTRWATCH ISO27001 Yazılımı
 
Jmeter ile uygulama katmanında yük testi gerçekleştirme
Jmeter ile uygulama katmanında yük testi gerçekleştirmeJmeter ile uygulama katmanında yük testi gerçekleştirme
Jmeter ile uygulama katmanında yük testi gerçekleştirme
 
ISO 27001:2013 versiyonu ile gelen değişiklikler
ISO 27001:2013 versiyonu ile gelen değişikliklerISO 27001:2013 versiyonu ile gelen değişiklikler
ISO 27001:2013 versiyonu ile gelen değişiklikler
 
Android Uygulamaların Tersine Mühendislik Yöntemi ile İncelenmesi
Android Uygulamaların Tersine Mühendislik Yöntemi ile İncelenmesiAndroid Uygulamaların Tersine Mühendislik Yöntemi ile İncelenmesi
Android Uygulamaların Tersine Mühendislik Yöntemi ile İncelenmesi
 

BTRisk X86 Tersine Mühendislik Eğitim Sunumu - Bölüm-2

  • 3. ÖN BİLGİ • Birinci aşama eğitmen eşliğinde gerçekleştirilecektir. Bu çalışma sırasında IDA'nın temel özellikleri ve kullanımına aşina olmanın yanı sıra tersine mühendisliğe uygulamalı bir giriş yapılacaktır. • Birinci aşamaya kısa bir "dinamik analiz" ile başlayacağız. • Daha sonra dinamik analizle tespit ettiğimiz "string" bilgilerini kullanarak uygulama içinde bu string'lerin kullanıldığı fonksiyonları inceleyecek ve hangi fonksiyona odaklanmamız gerektiğini tahmin etmeye çalışacağız. • Odaklanmamız gereken fonksiyonu tespit ettikten sonra da algoritma, veri yapıları ve fonksiyondan çağrılan API fonksiyonları ve diğer fonksiyonları inceleyerek fonksiyonun amacını anlamaya çalışacağız. • Karmaşık adımlarda fonksiyonu pseudo kodlayarak edindiğimiz algoritma bilgisini kaydedebiliriz.
  • 4. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER • IDA ismini Interactive Disassembler ifadesinden alır. Bir çalıştırılabilir dosya IDA tarafından ilk analiz edildiğinde IDA ".idb" uzantılı ayrı bir dosya üzerinde bir veritabanı oluşturur. • Bu noktadan itibaren disassembly üzerinde yapılan tüm işlemler (rename etmeler, renklendirmeler, gruplamalar, veri tipi dönüşümleri, v.d.) bu veritabanı üzerinde yapılır, çalıştırılabilir dosya ile ilgisi yoktur. Bu nedenle analistler bir zararlı üzerinde çalıştıklarında sadece IDA veritabanını paylaşarak çalışabilirler, çalıştırılabilir dosyanın paylaşılması gerekmez. • Tabi eğer analist isterse assembly kodu üzerinde yaptığı değişikliği çalıştırılabilir dosyaya yamalayabilir.
  • 5. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER • DİKKAT: IDA PRO'da "undo" (geri al) imkanı yoktur. Dolayısıyla belli aralıklarla yedek almakta fayda vardır. Zira veri yapılarını farklı formatlara dönüştürdüğümüzde (örneğin byte serisini string gibi yorumlattığımızda, v.b.), fonksiyon, parametre, değişken isimlerini rename ettiğimizde, veri alanlarını kod gibi yorumlattığımızda bu işlemleri geri almak vakit kaybettirebilir.
  • 6. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER IDA arayüzü hakkındaki temel bilgiler • Işıklandırma (highlighting) • IDA arayüzünde bir cursor'ü bir kelimenin üzerine (tıklayarak) konumlandırdığınızda arayüzde aynı kelimenin geçtiği diğer yerler de highlight edilir. • Örneğin "call" instruction'ına tıkladığınızda diğer "call" instruction'ları da highlight edilir. Ya da belli bir register'a atanan bir değerin nerede tekrar kullanıldığını kolayca görebilmek için bu register'ın adının üzerine tıklanabilir. • Bu sayede assembly kodu içinde bizim o anki incelememizle ilgili bölümler daha kolay ayrıştırılabilir.
  • 7. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER IDA arayüzü hakkındaki temel bilgiler • Işıklandırma (highlighting)
  • 8. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER IDA arayüzü hakkındaki temel bilgiler • Grafik Görüntü (Graph View) – Metin Görüntü (Text View) • IDA'nın en önemli faydalarından birisi Graph View imkanıdır. İki mod arasında "space" tuşu ile geçiş yapılabilir. SPACE
  • 9. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER IDA arayüzü hakkındaki temel bilgiler • Grafik Görüntü (Graph View) – Metin Görüntü (Text View) • Graph View bize fonksiyonun karmaşıklığı, ana dalları ve döngüleri hakkında görsel ve dolayısıyla daha kolay anlaşılabilir bilgi verir. • IDA döngüleri "kalın mavi" çizgilerle ifade eder. • Karar dalları (decision branches) eğer koşul doğru (true) ise "yeşil", değilse "kırmızı" çizgilerle ifade edilir.
  • 10. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER IDA arayüzü hakkındaki temel bilgiler • Grafik Görüntü (Graph View) – Metin Görüntü (Text View) Döngü if (*EAX = 0) == TRUEif (*EAX = 0) != TRUE
  • 11. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER Yeniden isimlendirme (renaming) • Assembly kodunu anlamlandırdıkça fonksiyon isimleri ve değişkenler üzerine tıklanarak "N" tuşuna basıldığında anlamlı isimler verebiliriz. Bu işlemi gerçekleştirdiğimizde söz konusu fonksiyonlara veya değişkenlere referans verilen diğer tüm noktalarda bizim yeni verdiğimiz isim görülür.
  • 12. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER Imports alt penceresi (subview) • PE dosya formatında da değinildiği gibi uygulamanın kullandığı işletim sistemi API'leri bize uygulamanın amaçları ile ilgili bilgi verebilir.
  • 13. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER Strings alt penceresi (subview) • Kesin bilgi vermese de dinamik analiz ile birleştirildiğinde string'ler hangi fonksiyonlara odaklanmamız gerektiği hakkında bize çok yardımcı olabilirler.
  • 14. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER Strings alt penceresi (subview) • Bir string'in üzerine çift tıklayarak onun dosya içindeki yerine ulaşabiliriz.
  • 15. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER Referanslar • String'e IDA tarafından verilen ismin üzerine tıkladıktan sonra "X" tuşuna basılırsa bu string'e referans verilen yerler görülebilir.
  • 16. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER Referanslar • Referanslar'dan birisi seçilerek çift tıklandığında assembly kodu içinde string'e referans verilen alana ulaşılır.
  • 17. ÖN BİLGİ IDA PRO HAKKINDA TEMEL BİLGİLER Geri dönme (ESC tuşu) • IDA içinde referanslar, fonksiyon isimleri, v.d. üzerine tıklanarak yapılan geçişlerde geri dönülmek için ESC tuşuna basılabilir. ESC
  • 18. 1. AŞAMA İlk olarak basit bir dinamik analiz ile başlıyoruz.
  • 19. 1. AŞAMA Dinamik analiz sırasında gözlemlediğimiz string'lerden birisini Strings penceresinden bularak çift tıklayalım.
  • 20. 1. AŞAMA IDA'nın C string'i olarak algıladığı bu veri alanına verdiği isme ("aTersineMuhendi") tıkladıktan sonra "X" tuşuna basarak bu alana referans veren diğer noktaları izleyelim.
  • 21. 1. AŞAMA String'imize veri alanına nazaran yukarıdaki [up] object, yani kod tipinde [o] bir alandan ve tam olarak da [_main] fonksiyonunun 0xBB offset adresinde bulunan bir instruction'dan referans verildiğini görüyoruz. Instruction'dan anlaşıldığı kadarıyla bu string'in adresi stack'te bir lokal değişken alanına yazılıyor. Şimdi çift tıklayarak bu kod bölümüne geçelim.
  • 22. 1. AŞAMA [Tersine muhendislik oyunumuza hos geldiniz.] string'ine referans verilen fonksiyonumuz bu fonksiyon.
  • 23. 1. AŞAMA Bu kod bölümünde biraz aşağıya doğru indiğimizde aşamalarla ilgili yönlendirme metinlerini görebiliyoruz. Bu fonksiyon bizim ana dallanma noktamız olabilir. Fonksiyon içinden çağrılan diğer fonksiyonları daha kolay görebilmek için [call] instruction'larından birisinin üzerine tıklayarak tüm [call] instruction'larını highlight edebiliriz. IDA bazı fonksiyonları [printf] olarak adlandırmış, bazılarını ise [sub_] ile başlayan ve yanında da RVA'e benzer bir adres ile isimlendirmiş.
  • 24. [printf] fonksiyonuna çift tıkladığımızda bir [stub] kod bölümüne atlıyoruz. Bu kodda sadece [Import Address Table - IAT]'da bulunan [printf] API fonksiyon adresine [JMP] instruction'ı bulunuyor. IDA'nın fonksiyon adı olarak [printf] yazabilmesinin sebebi de bu. 1. AŞAMA
  • 25. 1. AŞAMA [ASAMA-1] string'i stack'e yazıldıktan hemen sonra [printf] fonksiyonu çağrılıyor. Bundan sonra da [sub_401B19] fonksiyonu çağrılıyor.
  • 26. 1. AŞAMA [sub_401B19] fonksiyon adına tıkladığımızda bu fonksiyonun her [ASAMA] string'i yazıldıktan sonra çağrıldığını görebiliyoruz.
  • 27. 1. AŞAMA [sub_401B19] fonksiyon adına çift tıkladığımızda bu fonksiyona ulaşabiliriz. Fonksiyonun içeriğindeki metinlerden yola çıkarak kesin emin olmasak da bu fonksiyonun okumayla ilgili olduğu tahminini yapabiliriz.
  • 28. 1. AŞAMA Bu fonksiyonun adını tahminimizi yansıtacak biçimde [GIRDI_OKU] olarak [RENAME] edelim. Eğer hatalıysak daha sonra daha detaylı bir inceleme yaparak bu adı değiştirme imkanımız var.
  • 29. Fonksiyon adını değiştirdikten sonra fonksiyonun görünümü. 1. AŞAMA
  • 30. Yaptığımız isim değişiklikleri, eklediğimiz yorumlar, kod değişiklikleri v.d. bilgiler daha önce de belirttiğimiz gibi IDA'nın oluşturduğu veritabanına yazılır. 1. AŞAMA
  • 31. 1. AŞAMA ESC tuşu ile [GIRDI_OKU] fonksiyonundan bu fonksiyonu çağıran ana fonksiyona döndüğümüzde fonksiyonun çağrıldığı her noktada isminin [GIRDI_OKU] olarak güncellendiğini görebiliriz.
  • 32. 1. AŞAMA [GIRDI_OKU] fonksiyonundan bir sonra çağrılan fonksiyon [sub_401470] fonksiyonu. [Calling Convention]'lardan bahsederken [EAX] register'ının genellikle fonksiyonun [RETURN] değerini içerdiğini belirtmiştik. [GIRDI_OKU] fonksiyonu çağrıldıktan hemen sonra [EAX] register'ının değeri önce [var_4] lokal değişkenine, oradan tekrar anlamsız bir biçimde [EAX] register'ına atanıyor. Belli ki kullanılan derleyici bu konuda bir optimizasyon yapmamış veya yapamamış. Daha sonra [EAX] register'ının değeri [var_18] değişkenine atanıyor ki; bu değişken de [ESP]'nin işaret ettiği en uç noktada bulunan bir alanda yer alıyor. X86'ya giriş bölümünde bir fonksiyona [PUSH] instruction'ı ile parametre vermiştik. Burada ise derleyici yine ilginç bir farklılık göstererek stack'te daha önceden ayırmış olduğu alanın son bölümünü parametre aktarmak için kullanıyor.
  • 33. [sub_401470] fonksiyonuna geçtiğimizde [asdf2009] string'inin adresini [var_4] lokal değişkenine, aldığı parametreyi ise [var_8] lokal değişkenine atadığını görüyoruz. Bu işlemlerden hemen sonra da [sub_401492] fonksiyonu çağrılıyor. Bu fonksiyonun çıktısının [EAX register değerinin] [0] olup olmadığı kontrol ediliyor. Eğer [0] değilse [sub_401C1E] fonksiyonu çağrılıyor. Sonuç [0] olduğu durumda da fonksiyon normal bir biçimde sonlanıyor. 1. AŞAMA
  • 34. 1. AŞAMA Sonucun [0] olmadığı durumda çağrılan fonksiyona baktığımızda son olarak [exit] fonksiyonunun çağrıldığını görüyoruz.
  • 35. 1. AŞAMA C ve C++ API fonksiyonlarına MSDN'den veya diğer kaynaklardan göz atabiliriz. Bu kaynaklarda "exit" fonksiyonu çağrıldığında [process]'in, yani uygulamamızın kapatıldığını görebiliriz.
  • 36. 1. AŞAMA Bu fonksiyonu [HATALI_DENEME] olarak isimlendirelim. Yaptığı iş sadece [8] statü koduyla uygulamayı sonlandırmak.
  • 37. 1. AŞAMA Çıkış yapan fonksiyonu [RENAME] ettikten sonra kodu biraz daha rahat okunur hale getirmiş olduk.
  • 38. 1. AŞAMA İlk adımda çağrılan fonksiyonun adını da [ASAMA_1] olarak rename edebiliriz.
  • 39. 1. AŞAMA [ESC] tuşuyla ana fonksiyona göndüğümüzde çağrılan fonksiyon adının da güncellendiğini görebiliriz.
  • 40. 1. AŞAMA Uygulamanın anlaşılabilirliğini artırmak için [ASAMA_1] fonksiyonunun aldığı parametreye isim verelim.
  • 41. 1. AŞAMA Fonksiyonun aldığı parametreye [PRM_GIRDI] adını verelim.
  • 42. 1. AŞAMA [ASAMA_1] fonksiyonunda asıl kontrolün yapıldığını tahmin edebileceğimiz bir başka fonksiyon daha [sub_401A02] çağrılıyor. Bu fonksiyon çağrıldığında ise [ESP] register'ının işaret ettiği alanda [2] adet lokal değişkenin tutulduğunu görebiliyoruz.
  • 43. 1. AŞAMA Bu değişkenlerin de adlarını biraz daha anlamlı kılmak üzere [var_4]'ü [L_ASDF1234], [var_8]'i [L_GIRDI] olarak adlandırabiliriz. Eğer [sub_401A02] fonksiyonu [2] adet parametre alıyorsa bunlardan ilki [L_GIRDI] (yani kullanıcının girdiği veri), ikincisi ise [L_ASDF1234], yani [asdf2009] değeridir.
  • 44. 1. AŞAMA Tam olarak ne yaptığını analiz edebilmek için [sub_401A02] fonksiyon adına çift tıklayarak bu fonksiyona geçelim.
  • 45. 1. AŞAMA Bu fonksiyon önce 1. parametreyi [arg_0], daha sonra da [2.] parametreyi [arg_1] [sub_4019D6] fonksiyonuna parametre olarak veriyor ve bu fonksiyonu çağırıyor.
  • 46. 1. AŞAMA Bu fonksiyona geçtiğimizde kendisine verilen parametreyi [EAX] register'ına atadığını görüyoruz. [EAX] register'ının geçtiği yerleri daha rahat görebilmek için bu register'a tıklayalım. [var_8] lokal değişkeni kendisine verilen parametre ile initialize edilirken [var_4] [0] değeri ile initialize ediliyor. Aşağıdaki döngüde [var_4] sayaç olarak kullanılıyor. [var_8] lokal değişkeninin işaret ettiği bellek alananındaki byte ise her turda [0] ile karşılaştırılıyor. Son turda [var_4] sayaç değeri [EAX] register'ına atanarak geri döndürülüyor.
  • 47. 1. AŞAMA Yaptığımız analize dayanarak bu fonksiyonun kendisine parametre olarak verilen bir C string'inin uzunluğunu belirlediğini söyleyebiliriz. Bu nedenle bu fonksiyona [MTN_UZUNLUK] adını verebiliriz.
  • 48. 1. AŞAMA [ESC] tuşu ile geri döndüğümüzde bu fonksiyonun amacını anlamış olduğumuzdan, fonksiyona parametre olarak verilen 1. ve 2. parametrelerin (yani C string'lerinin) uzunluklarının karşılaştırılması neticesinde eğer bu string'lerin uzunlukları eşitse sağdaki kod alanlarına geçildiğini daha rahat görebiliyoruz. Bunu da kod okunurluğunu artırabilmek için [JUMP] edilen bu alanın ismini [UZUNLUK_ESITSE] ifadesiyle değiştirerek koda aktarabiliriz.
  • 49. 1. AŞAMA [sub_401A02] fonksiyonu aldığı 2 parametreyi [var_8] ve [var_C] lokal değişkenlerine atadıktan sonra bunlar üzerinde bir inceleme yapıyor. Bu parametrelerin ne olduklarını daha net görebilmek için [ESC] tuşuna basarak [ASAMA_1] fonksiyonuna geçebiliriz.
  • 50. [ASAMA_1] fonksiyonuna geçtiğimizde [1.] parametrenin bizim girdiğimiz değer, [2.] parametrenin ise [asdf2009] string'i olduğunu görebiliriz. Uygulama derleyicisinin çağrılan fonksiyonlara parametre aktarma yönteminin stack'te lokal değişkenler için ayrılan yerleri kullanmak olduğunu daha önceden değerlendirmiştik. 1. AŞAMA
  • 51. 1. AŞAMA [sub_401A02] fonksiyonunun içinde de parametre (argüman) isimlerini bu bilgilere uygun biçimde düzenleyebiliriz. Buna göre [1.] parametreye [TEST_GIRDISI], [2.] parametreye [MTN_ASDF2009] adını verebiliriz.
  • 52. Parametre isimlerini düzenlediğimizde kodun aşağı bölümlerinde değeri atanan lokal değişkenlerin de kullanım amaçlarını daha iyi anlayabiliriz. 1. AŞAMA
  • 53. Kodun kalan bölümünde lokal değişkenler üzerinden işlem yapıldığı için lokal değişkenleri [var_8] için [L_TEST_GIRDISI] ve [var_C] için [L_MTN_ASDF2009] olarak düzenleyelim. 1. AŞAMA
  • 54. Fonksiyonun içinde test girdisinin her bir byte'ını kontrol eden bölümde eğer okunan byte [0] ise Jump edilecek adresi [GIRDI_SONUNA_GELDIK] olarak adlandırabiliriz. 1. AŞAMA
  • 55. Girdinin son byte'ına gelinmemesi durumunda test girdisi ve [asdf2009] string'lerinin her bir byte'ının karşılaştırıldığı kod bölümüne geliyoruz. Bu karşılaştırmanın sonucunun eşitlik olmaması durumunda atlanan kod adresini de [GIRDI_FARKLI] olarak isimlendirebiliriz. 1. AŞAMA
  • 56. Karakterlerin aynı olması halinde test girdisi ve [asdf2009] string'i içinde işaret edilen byte'ların adresleri [EAX] register'ına atanır. 1. AŞAMA
  • 57. Test girdisi ve [asdf2009] string'i içinde işaret edilen byte'ların adresleri [INC] instruction'ı ile [1] artırılır ve döngünün bir sonraki turuna geçilir. 1. AŞAMA
  • 58. [sub_401A02] fonksiyonuyla ilgili analizimiz sonucunda bu fonksiyonun adını [MTN_KARSILASTIR] olarak düzenleyebiliriz. Eğer karşılaştırılan metin'ler aynı ise bu fonksiyon [0] değerini, farklı ise [1] değerini döndürerek sonlanıyor. 1. AŞAMA
  • 59. [ASAMA_1] fonksiyonuna geri döndüğümüzde ve çağrılan fonksiyonun amacının metin karşılaştırma olduğunu ifade eden bir fonksiyon adını gördüğümüzde girilecek olan girdinin [asdf2009] string'i olması gerektiğini net olarak söyleyebiliriz. 1. AŞAMA
  • 60. Bu bilgiyi kullanarak eğer [MTN_KARSILASTIR] fonksiyonunun döndürdüğü değer [0] ise atlanan (JUMP edilen) adresi de [METINLER_AYNI] şeklinde isimlendirebiliriz. 1. AŞAMA
  • 61. 1. AŞAMA Tahminimiz olan [asdf2009] metnini denediğimizde haklı olduğumuzu görebiliriz.
  • 62. NE ÖĞRENDİK IDA Pro hakkında • Strings penceresi • Kod highlighting (ışıklandırma) • XREF'ler ile kod v.d. bölümlere atlama • [ESC] tuşu ile geri dönme • Rename ederek fonksiyon, değişken, v.d. bölümleri anlamlandırma • IDB veritabanı dosyaları ve yedekleme ihtiyacı
  • 63. NE ÖĞRENDİK Algoritma hakkında • Döngüler, döngü çıkış koşulları testi ve "i++", yani [INC] kod örnekleri • "If" koşulu assembly kod örnekleri ("test eax, eax", "cmp eax, ebx", v.d.) X86 ve diğer • Sistem API'leri ile ilgili bilgi bulma (MSDN, diğer) • Üzerinde çalıştığımız kodu üreten derleyicinin çağrılan fonksiyonlara parametre aktarma yolu olarak en başta ayrılan stack alanını kullanmayı tercih ettiği
  • 65. ÖN BİLGİ [FOR] DÖNGÜ YAPISI for (i=0;i<6;i++) {} [FOR] döngüleri 3 ana bölümden oluşur: • Sıfırlama [initialization] • Test • Sayaç artırma
  • 66. ÖN BİLGİ DİZİ [ARRAY] ERİŞİMİ Dizi erişimleri [FOR] döngüleri içinde sıklıkla rastlanır çünkü dizi elemanlarının tamamını işlemek gerektiğinde döngüye ihtiyaç olaraktır. Genel dizi elemanı erişim şekli aşağıdaki gibidir: [BASE+COUNT*INCREMENT] BASE, dizinin başlangıç adresidir. COUNT, dizinin erişilen bileşeninin indeksidir. INCREMENT, dizi bileşeninin uzunluğudur.
  • 67. ÖN BİLGİ DİZİ [ARRAY] ERİŞİMİ [BASE+COUNT*INCREMENT] Örneğin; bir INTEGER array erişiminde bileşen uzunluğu [4] BYTE olaraktır ve bileşenlere yukarıdaki gibi erişilecektir.
  • 68. ÖN BİLGİ DEĞİŞKEN SAYIDA GİRDİ ALAN [API] FONKSİYONLARI Bazı API fonksiyonları işlevleri gereği değişken sayıda girdi alabilirler. Örneğin; [printf], [scanf], v.b. fonksiyonlar ilk parametreleri olan [format string]'e uyumlu olarak diğer parametreleri stack'te bulmayı beklerler. printf("%d, %d n", i, j);
  • 69. Ana fonksiyonda farklı aşamaların çağrıldığı noktaları (printf fonksiyonu ile) konsola yazılan mesajlardan yola çıkarak tahmin edebiliriz. Tahminimiz hatalı olsa bile değiştirme imkanımız var. 2. AŞAMA
  • 70. 2. AŞAMA Öncelikle fonksiyonun aldığı parametreyi daha belirgin hale getirmek amacıyla [RENAME] edelim.
  • 71. 2. AŞAMA Fonksiyonun aldığı parametreyi kopyaladığı lokal değişkeni de [RENAME] ederek teste tabi tutulacak olan verinin izini sürmeye devam edelim.
  • 72. 2. AŞAMA [HATALI_DENEME] fonksiyonunun çağrılmasına neden olabilecek JUMP instruction'ının öncesinde [var_38] lokal değişkeninin [1] değeri ile karşılaştırıldığını görüyoruz. Bu karşılaştırmadan hemen önce de [sub_401974] adlı fonksiyonun çağrıldığını görüyoruz. Muhtemelen bu fonksiyonun içinde yapılan bir kontrol bu aşamayı geçip geçmememize etkide bulunacak. Dikkat edersek yukarıda [var_38] lokal değişken alanının adresinin [var_44] lokal değişken alanına aktarıldığını görebiliriz. Bu da [sub_401974] fonksiyonu çağrıldığında [var_48] , yani AŞAMA 2'nin girdisiyle birlikte [var_38] lokal değişkeninin adresinin de fonksiyona parametre olarak verildiği ihtimalini doğuruyor.
  • 73. 2. AŞAMA [sub_401974] fonksiyonuna geçtiğimizde bu fonksiyonun [2] parametre aldığı tahminimizin doğru olduğunu IDA'nın yaptığı [arg_0] ve [arg_4] parametre çıkarımlarından görebiliriz.
  • 74. 2. AŞAMA Fare imlecini [L_PRM_GIRDI] adını verdiğimiz alanın üzerine getirdiğimizde ve beklediğimizde bir başka fonksiyonu çağırdığımızda bu fonksiyonun alacağı argümanların sırasını da yukarıdan aşağıya doğru görebiliriz. Buna göre [L_PRM_GIRDI] -> [arg_0] ise [var_44] -> [arg_4] olmalıdır.
  • 75. 2. AŞAMA Edindiğimiz bilgileri yansıtmak için [ASAMA_2]'nin [var_44] değişkenini de (fonksiyona verilen [2.] parametre anlamında) [F_PRM_2_PTR] olarak isimlendirebiliriz.
  • 76. 2. AŞAMA Çağrılan [sub_401974] fonksiyonuna verilen [2.] parametre bir pointer. Pointer'ın işaret ettiği bellek alanı ise [var_38]'di. Bu alanı da fonksiyon parametresi ile ilişkilendirmek için adını [L_PRM_2] olarak değiştirebiliriz.
  • 77. 2. AŞAMA Çağrılan [sub_401974] fonksiyonunun aldığı parametreleri de anlamlandırmak için [arg_0] ve [arg_4] adlarını da değiştirelim.
  • 78. 2. AŞAMA Fonksiyonun aldığı birinci parametre bizim ASAMA 2'de gireceğimiz girdi, ikinci parametre ise bu fonksiyonun bir şekilde kullanacağı bir bellek alanının adresidir. [ASAMA_2] fonksiyonunda kullandığımız isimlere benzer olarak birinci parametreye [PRM_GIRDI], ikinci parametreye ise [PRM_2_PTR] adını verebiliriz.
  • 79. 2. AŞAMA [sub_401974] fonksiyonunun içinden [sscanf] API fonksiyonunun çağrıldığını görüyoruz. IDA PRO sistem API fonksiyonlarını tanıdığı için aldığı parametreleri gösteriyor.
  • 80. 2. AŞAMA Ancak [sscanf] fonksiyonunu daha iyi tanımak için MSDN veya diğer C ve C++ kaynaklarından faydalanabiliriz. Buna göre [1.] parametre [sscanf] tarafından okunacak olan C string'ine işaret ediyor, [2.] parametre okunan C string'indeki verilerin hangi formatta değerlendirileceğini belirtiyor, diğer parametreler ise format string'de belirtilen verilerin yazılacağı alanların [pointer]'larından oluşuyor.
  • 81. 2. AŞAMA [sscanf] fonksiyonu çağrıldığında alacağı parametreleri görebilmek için lokal değişken isimleri üzerinde mouse imlecimizi bekletebiliriz. Buna böre [1.] parametre [var_28], [2.] parametre [var_24] ve [3.] parametre de [var_20] de yer almalı. Şimdi bu parametrelere atanan değerleri inceleyebilir ve isimlerini bizim için daha anlamlı isimlerle değiştirebiliriz.
  • 82. 2. AŞAMA Fonksiyonun aldığı [1.] parametre olan [PRM_GIRDI] değeri (ki bu değer aynı zamanda [ASAMA_2] fonksiyonun aldığı girdi oluyor) [sscanf]'in ilk parametresi oluyor. Bu parametreye [CHAR_PTR] adını verelim. [2.] parametre [%d %d %d %d %d %d] değeri olan ve 6 adet integer pointer'ına işaret eden bir string. Bu parametreye [FORMAT_STR_PTR] adını verelim. [sscanf] fonksiyonuna verilen [3.] parametre ise içinde bulunduğumuz [sub_401974] fonksiyonuna [2.] parametre olarak verilmiş olan bir pointer, bu alana da [L_PRM_2_PTR_INT] adını verelim. Demek ki [ASAMA_2] fonksiyonundan parametre olarak verilen lokal değişken adresi 6 integer değer adresinden ilkinin adresiymiş.
  • 83. 2. AŞAMA Windows sistem API dokümantasyonundan [sscanf] fonksiyonunun çıktısının doldurmayı başardığı parametre sayısı olduğunu anlıyoruz. Daha önceki incelememizden aldığı format string'de 6 adet [%d], yani integer değer referansı bulunduğunu söyleyebiliriz. [sscanf]'in çıktısının atandığı [var_4] lokal değişkeni [5] rakamı ile karşılaştırılıyor.
  • 84. 2. AŞAMA Eğer fonksiyonun aldığı string içinde [5] veya daha az parametre varsa oyunu kaybediyoruz. Kodun okunabilirliğini artırmak için okunan parametre sayısının [5]'ten büyük olduğunda atlanan adresi [OKUNAN_DEGER_SAYISI_5TENBUYUK] olarak adlandırabiliriz.
  • 85. 2. AŞAMA [sub_401974] fonksiyonunu inceledikten sonra kullanım amacının 6 adet INTEGER değer okumak olduğunu söyleyebiliriz. Bu nedenle fonksiyonun adını [ALTI_INT_OKU] olarak değiştirebiliriz.
  • 86. 2. AŞAMA [ALTI_INT_OKU] fonksiyonu kendisine [2.] parametre olarak verilen alandan başlayarak arka arkaya [6] adet INTEGER değeri belleğe dolduruyor olmalı. Bunlardan ilkinin değerinin [1]'den farklı olması halinde oyun sonlanıyor. Dolayısıyla [2.] aşamada girdiğimiz girdinin ilk rakamı [1] olmalı.
  • 87. 2. AŞAMA Bu tespitimizi kalıcı hale getirmek için ilk değerin [1] olduğu durumda atlanan adrese [BIRINCI_DEGER_BIR_ISE] adını verebiliriz.
  • 88. 2. AŞAMA Birinci değerin [1] olması koşulunu sağladıktan sonra karşımıza tipik bir döngü çıkıyor. Döngünün başında [var_C] lokal değişkenimize [1] değerinin atandığı ve bu değişkenin sayaç olarak kullanıldığını söyleyebiliriz.
  • 89. 2. AŞAMA [var_C]'nin sayaç olduğunu tahmin ettiğimize göre bu değişkenin adını [SAYAC] olarak değiştirebiliriz.
  • 90. 2. AŞAMA Döngünün çıkış şartı ise sayacın [5]'ten büyük olması. Eğer girdiğimiz [6] rakam da algoritmanın beklediği değerlere uygunsa [ASAMA_2] fonksiyonu başarılı bir şekilde sonlanacaktır. PSEUDO CODE for (int i=1; i>5; i++) { ... }
  • 91. 2. AŞAMA Algoritmanın kritik olan bölümü biraz karışık. Ancak yapılan karşılaştırma ilk parametreden [2] sonraki integer değerinin [1] sonraki değerin [2] katı olup olmadığının kontrolüdür. Daha sonraki döngülerde de bu karşılaştırma bir kaydırılarak gerçekleştirilir. [L_PRM_2] okunan değerlerin atandığı dizinin ilk elemanının adresi ise [2.] elemanın adresi [var_3C] olacaktır.
  • 92. 2. AŞAMA PSEUDO CODE if (arr[0] == 1) { for (int i=1; i>5; i++) { if (arr[i+1] != 2*arr[i]) { HATALI_DENEME(); } } } else { HATALI_DENEME(); } Karmaşık bir algoritmayı ifade etmenin en iyi yolu pseudo kod ile ortaya koymaktır. CEVAP: 1 2 4 8 16 32
  • 93. 2. AŞAMA Uygulama ile biraz daha oynadığımızda eğer uygulamaya bir parametre verirsek bize bir kullanım mesajı verdiğini görebiliriz. Bu mesaja göre uygulamamız bir dosyayı da girdi olarak kabul ediyor gibi görünüyor.
  • 94. 2. AŞAMA Benzeri bir tahmini uygulamanın string'lerini inceleyerek de yapabilirdik.
  • 95. 2. AŞAMA Bu tahminimizi test etmek için [1.] aşamadaki cevabımızı bir dosyaya yazarak uygulamaya verelim. Cevaplarımızı yazdıktan sonra bir defa [ENTER]'a basmayı unutmayalım. Bu şekilde sonraki aşamalarda önceki cevapları tekrar tekrar yazmaktan kurtulabiliriz.
  • 96. 2. AŞAMA Uygulamaya parametre olarak dosya verdiğimizde de uygulamanın sağlıklı olarak çalıştığını görebiliriz.
  • 97. 2. AŞAMA [2.] yanıtımızı da dosyanın içine yazarak uygulamamızı çalıştıralım.
  • 98. 2. AŞAMA [2.] aşamadaki tahminimizin de doğru olduğunu görebiliriz.
  • 99. NE ÖĞRENDİK Algoritmalar hakkında • [FOR] döngüleri assembly kod yapısı • Sayaç sıfırlama • Test • Sayacı artırma Diziler [Array] hakkında • Dizi bileşenlerine erişim için adres hesaplama [TABAN+SAYAC*BOYUT] API fonksiyonları hakkında • Değişken parametre alabilen API fonksiyon örnekleri
  • 100. NE ÖĞRENDİK Analiz hakkında • [PSEUDO KOD] yöntemi ile analiz edilen algoritmaları dokümante etme
  • 102. ÖN BİLGİ [SWITCH .. CASE] DEYİMİ VE [JUMP TABLE] Switch .. Case deyimi farklı derleyiciler tarafından farklı şekillerde assembly'ye dönüştürülür. Bazı derleyiciler iç içe [if .. elseif .. elseif .. else] şeklinde derlerken bazıları da bir Jump Table dizisi kullanabilir. Yukarıdaki kod bloğunda [EAX] register'ının barındırdığı adrese [JUMP] etmeden önce [var_4] değişkeninin değeri (ki muhtemelen bir indeks değeri) [SHIFT LEFT] instruction'ı ile [4] ile çarpılıyor. Bu offset'te bir [JUMP TABLE] içinde ilgili adresi bulmak için kullanılıyor.
  • 103. 3. AŞAMA 3. aşama fonksiyonumuz da bir önceki aşamada olduğu gibi [sscanf] fonksiyonunu çağırıyor. Fakat bu defa format string'imiz farklı [%d %c %d], yani [INT], [CHAR], [INT] formatında veriler okunuyor.
  • 104. 3. AŞAMA Daha önce de yaptığımız gibi parametre ve lokal değişken isimlerini daha anlaşılır kılmak için [RENAME] edebiliriz. İlk olarak format string pointer'ının saklandığı lokal değişkenin ismini değiştirelim.
  • 105. 3. AŞAMA Diğer aşamalar gibi alınan parametrenin ve bu parametrenin atandığı lokal değişkenlerin isimlerini değiştirelim.
  • 106. 3. AŞAMA Bu noktaya kadar karşımızdaki derleyicinin yaklaşımına alışmış olmamız lazım. [sscanf] çağrıldığında Girdi ve format string'den hemen sonra [3] adet pointer gelmeli. Bunlar [var_20], [var_1C] ve [var_18]. Ancak bu adreslerde pointer'ları saklanan lokal değişken alanları ise: • [var_4] [int] • [var_E] [char] • [var_8] [int]
  • 107. 3. AŞAMA Lokal değişken adları olarak aşağıdakileri kullanabiliriz: • [var_4] [int] – INT_1 • [var_E] [char] – CHAR • [var_8] [int] – INT_2
  • 108. 3. AŞAMA Önceki aşamadan [sscanf]'in döndürdüğü sonucun okunan veri sayısı olduğunu biliyoruz. Uygulamanın sonlanmaması için en az [3] değer okunması lazım.
  • 109. 3. AŞAMA Okunan ilk [INTEGER] değer [7] rakamıyla karşılaştırılıyor ve eğer [7]den büyükse [JA] [Jump if Above] instructionı ile oyun sonlanıyor. Buna göre ilk değer integer olmalı ve [7] veya daha küçük bir değer olmalı.
  • 110. 3. AŞAMA Doğru yolda ilerlerken ilginç bir olay gerçekleşiyor. Grafik olarak kod sanki sona gelmiş gibi görünüyor. Gerçekte olan ise [ds:off_404264] adresinden itibaren 1. INTEGER değerin 4 katı [shl eax,2] offset'te bulunan adrese [JMP] ediyor olmamız. Bu akış derleyicimizin [SWITCH .. CASE] statement'ını yorumlama şekli.
  • 111. 3. AŞAMA [ds:off_404264] adresine atladığımızda toplam 8 adres barındıran bir JUMP TABLE görüyoruz. Yani [0-7] arasındaki bir rakamın [4] ile çarpıldığında bulunabilecek offset'lerde diğer kod bölümlerinin adreslerini görüyoruz.
  • 112. 3. AŞAMA Jump table'ımızın ilk değeri olan olan [loc_40153F] değerine geçelim [1. INT değerinin "0" olduğu senaryo için]
  • 113. 3. AŞAMA [EBP – 0xD] adresine [0x62] değeri yazıldıktan sonra [EBP – 0x8] offset'indeki değer [0x65] ile karşılaştırılıyor. Fonksiyonun yukarı kısımlarına bakarsak daha önceden bu alanın 2. INTEGER değerin saklanması için kullanıldığını görebiliriz. Her nedense IDA PRO bu alana verdiğimiz ismi burada kullanamamış.
  • 114. 3. AŞAMA IDA'da bir değeri Decimal karşılığına dönüştürmek için Context Menü'yü veya [H] tuşunu kullanabiliriz.
  • 115. 3. AŞAMA 1. INTEGER değerinin [0] olduğu durumda 2. INTEGER değeri [101] ile karşılaştırılıyor.
  • 116. 3. AŞAMA Pek doğru bir isimlendirme olmasa da 2. INTEGER değerinin [101] olması halinde [JMP] edilen lokasyonun adını [IKINCI_INT_DEGER_101_ISE] olarak belirleyebiliriz. İsim çok uygun olmasa da tüm CASE dallarından hata alınmadan atlanması gereken kod bölümünü daha rahat görebiliyoruz.
  • 117. 3. AŞAMA Son aşamada [EBP + var_D] offset'inde bulunan değer gireceğimiz ikinci değer olan [CHAR] tipindeki [1] byte'lık değer ile karşılaştırılıyor. [var_D] lokal değişkeninin değeri ise yukarıda atanmıştı.
  • 118. 3. AŞAMA Geldiğimiz yola geri dönersek 1. INT değerinin [0] olduğu durumda [var_D] lokal değişkenine atanan değer [0x62] imiş.
  • 119. 3. AŞAMA 2. olarak gireceğimiz verinin tipi karakter tipinde bir veri olduğu için [0x62] değerini context menü ile veya [R] harfine basarak ASCII karaktere dönüştürelim
  • 120. 3. AŞAMA Seçtiğimiz dal için geçerli olan veriler aşağıdaki gibi: 1. INT – [0] 2. CHAR – [b] 3. INT – [101]
  • 121. 3. AŞAMA Girdi dosyamıza aşağıdaki satırı ekleyelim (veya siz seçtiğiniz dal için belirlediğiniz rakamları girebilirsiniz): 0 b 101
  • 123. NE ÖĞRENDİK Analiz hakkında • Switch .. Case deyiminin assembly kodunda Jump Table kullanılarak uygulanma şekli • Analiz ettiğimiz kodun kapsamını ihtiyaçlarımıza göre çizebilme, girdi ve çıktıları dikkate alarak analiz zamanımızı ekonomik kullanabilme
  • 125. ÖN BİLGİ RECURSIVE [ÖZYİNELİ] FONKSİYONLAR Kendini çağıran fonksiyonlara verilen isimdir. Genellikle aşağıdaki algoritmaların uygulanmasında karşılaşılırlar: • Böl ve fethet algoritmalarında • Sıralama (sorting) algoritmalarında • Dizin, file system arama [tree ve graph traversal] gibi algoritmalarında Gözle analizleri çok zor olduğu için analiz sırasında [PSEUDO CODE] yöntemi ile algoritmanın yazılmasında büyük fayda vardır.
  • 126. 4. AŞAMA Ana fonksiyonda 4. aşama olarak tahmin ettiğimiz fonksiyonun içine girelim.
  • 127. 4. AŞAMA Önceki fonksiyonlara benzer biçimde yine okunan veriye bir referans parametre olarak alınıyor. Yine [sscanf] fonksiyonu çağrılıyor ancak bu defa 2 INTEGER değer okunmaya çalışılıyor.
  • 128. 4. AŞAMA [arg_0] parametresinin adını daha anlaşılır olması amacıyla [PRM_GIRDI] olarak değiştirelim.
  • 129. 4. AŞAMA Alınan parametrenin kopyalandığı lokal değişkenin, format string'in adresinin atandığı lokal değişkenin ve "sscanf" fonksiyonunun okuduğu değerleri yerleştirdiği lokal değişkenlerin adlarını önceki çalışmamızda olduğu biçimde değiştirelim.
  • 130. 4. AŞAMA Kodun bu bölümünü okuyarak okunan parametre sayısının [2] olması gerektiğini, okunan 2. INTEGER'ın [ 1 < INT_2 < 5 ] aralığında olması gerektiğini söyleyebiliriz.
  • 131. 4. AŞAMA Derleyicimizin çağrılan fonksiyonlara parametre aktarmak için stackte ayrılmış olan bölümün son kısımlarını kullanmayı tercih ettiğini ve Visual Studio gibi PUSH instruction'ları ile parametre aktarımı gerçekleştirmediğini daha önce konuşmuştuk. Bu bölümde daha önce format string pointer'ını ve fonksiyona verilen parametrenin bir kopyasını saklamak için kullanılan lokal değişken alanlarının farklı amaçlarla kullanıldığını görebiliyoruz. Bu alanlar sırasıyla [INT_2] ve [5] değerlerini barındırarak çağrılan [sub_401603] fonksiyonuna parametre aktarmak amacıyla kullanılıyor. Bu bilgileri not edebilmek için IDA'nın comment özelliğini [;] kullanabiliriz.
  • 132. 4. AŞAMA Bu bölümde algoritmamızın başarılı sonuçlanabilmesi için çağrılan fonksiyonun döndürdüğü değer ile [INT_1] değerinin aynı olması gerektiğini görebiliriz.
  • 133. 4. AŞAMA Bu aşamayı çözebilmemiz için çağrılan [sub_401603] fonksiyonunda uygulanan algoritmayı çözmemiz lazım.
  • 134. 4. AŞAMA [sub_401603] fonksiyonu tahmin ettiğimiz gibi 2 parametre alıyor. 1. parametrenin [0] ve [1] olduğu durumlarda uygulama farklı dallardan ilerliyor. Bu biraz garip çünkü daha önce 1. parametrenin değerinin [5] olacağını görmüştük.
  • 135. 4. AŞAMA Fonksiyonun aldığı parametrelerin daha iyi anlaşılabilmesi için parametre isimlerini [PRM_1_ILK_DEGER_5] ve [PRM_2_INT_2] olarak değiştirelim
  • 136. 4. AŞAMA Fonksiyon dallarını daha iyi anlamlandırmak için 1. parametre'nin [0]'dan büyük olması halinde atlanan kod adresine [ILK_PARAMETRE_1_VE_DAHABUYUK] diyelim.
  • 137. 4. AŞAMA En sağdaki dal ise 1. parametrenin [2] veya daha üstü olduğu durumda işletiliyor. Bu nedenle bu dalın adresine de [ILK_PARAMETRE_2_VE_DAHABUYUK] adını verebiliriz.
  • 138. 4. AŞAMA 1. parametre'nin [0] olması halinde fonksiyon [0] değerini döndürüyor, [1] olması halinde ise [2. parametre'nin değeri]ni döndürüyor. (IDA PRO'da en alttaki kutuda [var_8] lokal değişkeninin değerinin [EAX]'e atandığını görebilirsiniz.) Bu tespitlerimizi not etmek için de [;] comment işaretini kullanarak ilgili kodların yanına kayıt düşebiliriz.
  • 139. 4. AŞAMA 1. parametre'nin "2" veya daha büyük olduğu koşulda devreye giren kod bölümünde [CALL] edilen fonksiyon adlarına baktığımızda fonksiyonun kendisini çağırdığını görüyoruz. Bu durum fonksiyona ilk parametre olarak "5" değerini sabit değer olarak vermemize rağmen neden [0] ve [1] değerleri ile ilgili kod bölümlerinin var olduğuna dair bize biraz ışık tutuyor. Belki de 1. parametre fonksiyonun sonraki recursive çağrılmaları öncesinde daha farklı 1. parametre değerleri alıyordur.
  • 140. 4. AŞAMA Fonksiyonumuzun tam olarak ne yaptığını henüz çözmüş değiliz, ancak onu şimdilik de olsa [RECURSIVE_FUNC] olarak değiştirebiliriz. Bu isim biraz da olsa kodun okunabilirliğini artıracaktır.
  • 141. 4. AŞAMA [RECURSIVE_FUNC] fonksiyonunun stack frame'ine göz attığımızda stack'in uç noktasında [var_10] ve [var_C] değişkenleri bulunduğunu görüyoruz.
  • 142. 4. AŞAMA Fonksiyonun 1. parametresine [M], 2. parametresine [N] dersek 1. recursive çağrının [M-1] ve [N] parametreleri ile yapıldığını görebiliriz.
  • 143. 4. AŞAMA [EBX] register'ı önce 1. recursive çağrının sonucunu saklamak için kullanılıyor. Daha sonra da [N] rakamı, yani 2. parametre [EBX] register'ına ekleniyor. Kodun aşağı bölümlerinde 2. recursive çağrının sonucunun da [EBX] register'ına eklendiğini ve toplam rakamın da [var_8] lokal değişkenine atandıktan sonra fonksiyon return değeri olarak döndürüldüğünü görebiliriz.
  • 144. 4. AŞAMA 2. çağrının 1. parametresi olarak da [M-2] rakamının kullanıldığını görebiliriz.
  • 145. 4. AŞAMA 2. recursive çağrının 2. parametresi olarak da [N] rakamı kullanılıyor. Yani 2. çağrı F(M-2,N) şeklinde gerçekleşiyor.
  • 146. 4. AŞAMA 2. recursive çağrının sonucu daha önce [EBX] register'ında saklanan veriye ekleniyor ve bu değer fonksiyonun çıktısı olarak döndürülüyor. Yani fonksiyonumuzu aşağıdaki gibi tanımlayabiliriz: F(M,N) = F(M-1,N)+F(M-2,N)+N
  • 147. KURALLAR Bu aşamayı geçmek için tespit ettiğimiz kuralları sıralarsak: • M: Birinci parametre • N: İkinci parametre • M=0 ise 0 döndür [F(0,N)=0] • M=1 ise N'i döndür [F(1,N)=N] • M=5'tir • N 2'den küçük ve 4'ten büyük olamaz • FORMÜL => F(M,N) = F(M-1,N) + F(M-2,N) + N 4. AŞAMA
  • 148. İlk parametre "5" olmak zorunda olduğuna göre fonksiyonun F(5,2) parametreleri ile vereceği yanıtı hesaplamaya çalışalım. • F(5,2)=F(4,2)+F(3,2)+2 • F(5,2)=[F(3,2)+F(2,2)+2]+[F(2,2)+2+2]+2 • F(5,2)=[[F(2,2)+2+2]+[2+0+2]+2]+[[2+0+2]+2+2]+2 • F(5,2)=[[[2+0+2]+2+2]+[2+0+2]+2]+[[2+0+2]+2+2]+2 • F(5,2)=24 4. AŞAMA
  • 149. 4. AŞAMA [RECURSIVE_FUNC] fonksiyonunun çağrıldığı [ASAMA_4] fonksiyonumuza dönersek recursive fonksiyonumuzdan dönen sonucun [ASAMA_4] fonksiyonuna verilen 1. INTEGER rakam ile aynı olması gerektiğini görebiliriz. 2. INTEGER değeri de recursive fonksiyona parametre olarak verilen 2. parametre değeri olacak.
  • 150. 4. AŞAMA Yaptığımız hesaplamalara göre 4. aşama için [24 2] girdileri başarılı olmalı.
  • 152. NE ÖĞRENDİK Recursive fonksiyonlar ve analizleri hakkında • Recursive fonksiyonların gözle analizi çok zor olduğundan [PSEUDO CODE] kullanılarak analiz edilmelerinde fayda vardır. • Girdilerin üreteceği çıktıları tahmin etmeye çalışırken fonksiyon çağrılarını daha fazla recursive çağrı yapılmayan durumlara kadar indirgemek gereklidir.
  • 154. ÖN BİLGİ STRING/VERİ DECODE ETME • Kodlama (encoding), kriptolama (encryption) gibi algoritmalarda kullanılan veri dönüştürme yöntemidir. • Okunan veri dönüştürüldükten sonra yine aynı adrese veya farklı bir adrese kopyalanarak yazılır. • Tek byte veya çoklu byte [WORD, DWORD] üzerinde işlem yapılabilir. • String decode etme durumlarında veri IDA'da [HEX] olarak görüntüleneceğinden veri tipini değiştirme veya bir ASCII tablo kullanma ihtiyacı olabilir. • Genel algoritma: • Bellekten oku • Dönüştür • Belleğe yaz
  • 155. ÖN BİLGİ BIT MASK İLE MASKELEME • Belli bir verinin belirli [bit]'lerini sabit bırakmak ve diğerlerini [0]'lamak için kullanılan yöntemdir. • Logical [AND] operatörü ve bir [Bit Mask] kullanılarak gerçekleştirilir. Yukarıdaki işlemde [EAX] register'ının son nibble'ı (4 bit'i) hariç tüm bit'leri sıfırlanacaktır.
  • 156. 5. AŞAMA Ana fonksiyonda 5. aşama olarak tahmin ettiğimiz fonksiyonun içine girelim.
  • 157. 5. AŞAMA IDA'nın da yardımıyla [ASAMA_5] fonksiyonunun da tek bir parametre aldığını görebiliyoruz.
  • 158. 5. AŞAMA Okunabilirliği biraz daha artırabilmek için alınan parametrenin adını [PRM_GIRDI] olarak değiştirelim.
  • 159. 5. AŞAMA Daha önce analiz ettiğimiz ve [MTN_UZUNLUK] olarak isimlendirdiğimiz fonksiyon tekrar karşımıza çıktı. Stack frame'in sonunda [var_38] alanı var ve fonksiyon bu adresteki veriyi parametre olarak alıyor olmalı.
  • 160. 5. AŞAMA [var_38] alanına [PRM_GIRDI] parametresi kopyalandığı için okunablirliği artırmak adına bu alanı [L_PRM_GIRDI] olarak değiştirelim.
  • 161. 5. AŞAMA Daha önceki analizimizden [MTN_UZUNLUK] fonksiyonunun kendisine parametre olarak verilen metnin içindeki karakter sayısını döndürdüğünü biliyoruz. Döndürülen değer [6] rakamı ile karşılaştırılıyor ve eğer [6]'dan farklı ise uygulamayı sonlandırmak üzere [HATALI_DENEME] fonksiyonu çağrılıyor.
  • 162. 5. AŞAMA Kodun devamına baktığımızda bir döngü görüyoruz ve [var_C] değişkeni sayaç olarak kullanılıyor. Döngünün başında [0] olarak belirlenen sayaç değeri her döngü de [5]'ten büyük mü diye kontrol ediliyor.
  • 163. 5. AŞAMA Anlaşılabilirliği biraz daha artırmak için [var_C] değişkeninin adını [SAYAC] olarak değiştirebiliriz.
  • 164. 5. AŞAMA [SAYAC] sağ alttaki kutuda bulunan [INC] instruction'ı ile [1] artırılıyor. for (i = 0; i < 6; i++){ ... }
  • 165. 5. AŞAMA Comment'lerle açıklanmış olan bu bölümde ilk 3 instruction sonucunda [var_28] alandan [SAYAC] offset'in adresi [EDX]e atanıyor. Bu adres biraz sonra yapılacak işlemin sonucunun yazılacağı adres olarak kullanılacak. 4,5,6. instruction'larda fonksiyonun girdi alanının (PRM_GIRDI) [SAYAC] offset'indeki BYTE değeri [0xF] değeri ile binary [AND] işlemine tabi tutuluyor. Yani bu byte'ın sadece düşük değerli yarısı [4 bit] [EAX] register'ında kalıyor.
  • 166. 5. AŞAMA Her bir döngüde okunan girdi karakterinin son 4 bit'i [0x403000] adresinden itibaren offset değeri olarak kullanılıyor ve bu offset'teki byte değeri [EAX]'e atanıyor.
  • 167. 5. AŞAMA [0x403000] adresine göz attığımızda burada bir dizi karakterin veri olarak bulunduğunu görüyoruz.
  • 168. 5. AŞAMA [0x403000] adresinden başlayan alandaki verilere göz attığımızda bu adresten itibaren belli bir offset'ten atanan [1] byte'lık verinin bir karakter olduğunu tahmin edebiliriz.
  • 169. 5. AŞAMA Atanmış olan karakter [var_28] adresinden itibaren her döngüde [1] byte ilerleyerek yazılıyor.
  • 170. 5. AŞAMA [var_28] adresinin kullanım amacıyla ilgili tespitimizi de yorum satırı olarak kaydedebiliriz.
  • 171. 5. AŞAMA Toplam 6 karakter [var_28] adresinden başlayarak yazıldıktan sonra döngü sonlanıyor ve [var_22] adresine [0] yazılmış. [0] yani [NULL] byte'ı C string'leri için string'in sonunu belirtiyor. Bir diğer deyişle [var_28] adresinden başlayan veri sonuna eklenen null byte ile bir C string'ine çevrilmiş.
  • 172. 5. AŞAMA [var_34] değişkenine [btrisk] string'inin adresi yazıldıktan sonra [var_28] alanının adresi [EAX] register'ına atanıyor.
  • 173. 5. AŞAMA Daha önceden analiz ettiğimiz [MTN_KARSILASTIR] fonksiyonu çağrıldığın da stack frame'in en üzerinde [var_38] ve [var_34] alanlarının bulunduğunu görebiliriz. Tabi daha önce [var_38]'i [L_PRM_GIRDI] adıyla değiştirmiştik, ancak bu alan burada farklı bir veriyi tutmak üzere kullanılacak.
  • 174. 5. AŞAMA [MTN_KARSILASTIR] fonksiyonumuz daha önceki analizimizden de bildiğimiz üzere kendisine verilen 2 string'i birbiri ile karşılaştırıyor.
  • 175. 5. AŞAMA Eğer [var_38]'de (şimdiki adı ile L_PRM_GIRDI) bulunan string [btrisk] string'inden farklı ise [MTN_KARSILASTIR] fonksiyonunun [1] döndürmesi gerekiyor (daha önceki analizimizde bunu tespit etmiştik).
  • 176. 5. AŞAMA 2 string birbirinden farklı ise uygulamamız sonlanıyor. Eğer üretilen string [btrisk] string'iyle aynıysa bu aşamayı başarı ile tamamlamış olmamız gerekiyor.
  • 177. 5. AŞAMA KURALLAR Bu aşamayı geçmek için tespit ettiğimiz kuralları sıralarsak: • 6 karakterli bir string girmeliyiz • Her bir karakterin byte değerinin son nibble'ını [son 4 bit'ini] 0x403000 adresinden itibaren offset olarak kullanarak bir karakter dizisi elde etmeliyiz • Elde ettiğimiz yeni karakter dizisi "btrisk" olmalı
  • 178. 5. AŞAMA Sondan başa doğru ilerlersek [btrisk] dizisini oluşturmak için önce doğru indeks'leri (offset'leri) bulmalıyız. Sıra:1 Indeks:13 [0xD] Sıra:2 Indeks:11 [0xB] Sıra:3 Indeks:6 [0x6] Sıra:4 Indeks:4 [0x4] Sıra:5 Indeks:7 [0x7] Sıra:6 Indeks:14 [0xE] 0xD 0xB 0x6 0x4 0x7 0xE
  • 179. 5. AŞAMA [0xDB647E] nibble'larını üretecek karakterleri bulmak için bir ASCII tablosundan faydalanabiliriz.
  • 180. 5. AŞAMA İstediğimiz sonucu verecek örnek bir karakter dizisi olarak [MKVT7N]'yi deneyebiliriz.
  • 182. NE ÖĞRENDİK Veri encode etme analizi hakkında • ASCII tablosundan faydalanma • Bit maskeleme • Veri dönüştürme (encode) etme örneği