3. ANALİZ
STATİK ANALİZ [Import Address Table]
Bir uygulamanın Import
ettiği fonksiyonlar o
uygulamanın
fonksiyonalitesine ilişkin
bize fikir verebilirler
4. ANALİZ
STATİK ANALİZ [Import Address Table]
Import edilen fonksiyon adlarını
kopyalayarak bir şüpheli fonksiyon
listesiyle karşılaştırabilmek için IDA
Pro'nun Imports penceresinde sağ
klikleyerek Copy seçeneğini seçelim
6. ANALİZ
STATİK ANALİZ [Import Address Table]
Şüpheli API listemizle Import
adres tablosunu karşılaştırmak
için MS Excel'in VLOOKUP
fonksiyonunu da kullanabiliriz
7. ANALİZ
STATİK ANALİZ [Import Address Table]
Bunun için fonksiyon adlarını
ayrı kolona bölmek amacıyla
Text to Columns özelliğini
kullanalım ve delimiter olarak
"Space"i seçelim
8. ŞÜPHELİ WINDOWS API'LERİ
Windows API Fonksiyonları
• Windows API fonksiyonlarına baktığımızda bazılarının “A” ile,
bazılarının ise “W” ile bittiğini görürüz. Bu fonksiyonlardan A ile
bitenler “string” veri tipinde aldıkları parametreleri ANSI (ASCII
değil) string’leri olarak, W ile bitenler ise Unicode (Wide
Character) string’leri olarak kabul ederler.
• Bazı fonksiyonların sonu da “Ex” olarak bitmektedir (bu
fonksiyonlarda da W ve A ekleri görülebilir, yani “Ex” ekinden
sonra W veya A gelebilir). Bu fonksiyonlar da benzer isimli
önceki fonksiyonların yerini almış ancak eski fonksiyonların
sahip oldukları fonksiyonaliteyi de destekleyen “Extended”
fonksiyonlardır.
9. ŞÜPHELİ WINDOWS API'LERİ
Şüphe Doğurabilecek API’ler
• Zararlı yazılımların genellikle ihtiyaç duydukları ve bu yüzden
şüpheli olabilecek API’lere sonraki sayfalardaki örnekleri
verebiliriz. Elbette API’lerin birden fazla kullanım amacı olabilir
ve liste daha da genişletilebilir, ancak kullanım amaçları ile ilgili
fikir vermesi için bu şekilde bir gruplama yapılmıştır.
• Elbette zararlının özel amaçları doğrultusunda ihtiyaç duyacağı
başka API’ler de olabilir. Tüm Windows API’lerine erişmek için
MSDN’den faydalanabilirsiniz.
10. ŞÜPHELİ WINDOWS API'LERİ
Ağ Erişimiyle İlgili API’ler
İhtiyaç: Zararlı yazılımlar veriyi dışarı sızdırmak, bir sunucuyla tünel
bağlantısı kurmak, ikinci aşama payload’larını indirmek, v.b.
ihtiyaçlarla ağ erişim API’lerini kullanabilirler.
WinSock API
• WSAStartup
• getaddrinfo
• socket
• connect
• send
• recv
• WSAGetLastError
11. ŞÜPHELİ WINDOWS API'LERİ
Ağ Erişimiyle İlgili API’ler (devamı)
WinINet API
• InternetOpen
• InternetConnect
• InternetOpenURL
• InternetReadFile
• InternetWriteFile
• HTTPOpenRequest
• HTTPQueryInfo
• HTTPSendRequest
12. ŞÜPHELİ WINDOWS API'LERİ
Ağ Erişimiyle İlgili API’ler (devamı)
Kernel32 API
• ConnectNamedPipe
• PeekNamedPipe
Urlmon API
• URLDownloadToFile
Diğer
• FtpPutFile
• GetAdaptersInfo (Anti-Virtual Machine kontrolü için de
kullanılabilir)
13. İhtiyaç: Zararlı yazılımlar dosya drop etmek (resource
section’larındaki bir dosyayı diske yazmak), kendilerini başka bir
isimle saklamak, veri sızdırmak için dosyalara erişmek, indirdiği ve
ihtiyacı kalmayan dosyaları silmek v.b. amaçlarla dosya erişim
API’lerini kullanabilirler.
• CreateFile
• ReadFile
• WriteFile
• FindResource
• LoadResource
ŞÜPHELİ WINDOWS API'LERİ
Dosya Erişimiyle ve Dropper
Fonksiyonalitesiyle İlgili API’ler
14. ŞÜPHELİ WINDOWS API'LERİ
Registry Erişimiyle İlgili API’ler
İhtiyaç: Zararlı yazılımlar kalıcılıklarını sağlamak için Autorun
registry değerlerine yazmak, parola v.b. hassas verilere erişmek,
varsa hedeflediği bir uygulamanın kurulu olup olmadığını anlamak,
v.b. amaçlarla registry API’lerini kullanabilirler.
• RegOpenKeyEx
• RegSetValueEx
• RegGetValue
15. ŞÜPHELİ WINDOWS API'LERİ
Keylogger Fonksiyonalitesiyle İlgili API’ler
İhtiyaç: Zararlı yazılımın keylogger fonksiyonalitesi var ise bu
bilgileri elde etmelerine imkan veren API’lere erişebilirler.
• GetAsyncKeyState
• GetForegroundWindow
• GetKeyState
• SetWindowsHookEx
• CallNextHookEx
• AttachThreadInput
• MapVirtualKey
16. ŞÜPHELİ WINDOWS API'LERİ
Proses İşlemleri ile İlgili API’ler
İhtiyaç: Zararlı yazılımlar diğer proses’lerin adres space’lerinde kod çalıştırmak
için, anti-virüs v.d. proses’leri öldürmek için, dinamik analizi engellemek için v.d.
amaçlarla bu API’lere ihtiyaç duyabilirler.
• CreateToolhelp32Snapshot (process listesini belirlemek için kullanılır)
• Process32First/Process32Next
• Module32First/Module32Next
• OpenProcess
• VirtualAllocEx
• VirtualProtectEx (bellek erişim haklarının değiştirilmesi için kullanılır)
• WriteProcessMemory
• AdjustTokenPrivileges
• CreateRemoteThread
• EnumProcesses
• EnumProcessModules
• IsWoW64Process
• QueueUserAPC
17. ŞÜPHELİ WINDOWS API'LERİ
DLL Yükleme ve Fonksiyon Adresi Bulma
API’leri
İhtiyaç: Zararlı yazılımlar detaylı statik analizde tespit edilebilecek
olmalarına rağmen Import Address Table’larında kullandıkları
kütüphane ve API’lerin görünmemesi amacıyla dinamik olarak
kütüphane yükleme ve bunların Export ettikleri fonksiyonların
adreslerini bulmak için gerekli API’leri kullanabilirler.
• LoadLibrary
• GetProcAddress
• LdrLoadDll
18. ŞÜPHELİ WINDOWS API'LERİ
Debug Edilip Edilmediğini Tespit Etmek
için Kullanılan API’ler
İhtiyaç: Zararlı yazılımlar debug edildiklerini tespit etmek ve normal
davranışlarını sergilememek için çok çeşitli teknikler kullanırlar.
Bunlardan bir kısmı Windows’un sağladığı debugger tespit
imkanlarıdır.
• IsDebuggerPresent
• CheckRemoteDebuggerPresent
• FindWindow (örneğin Ollydbg uygulamasının aktif olup olmadığını anlamak
için)
• GetTickCount
• NtQueryInformationProcess
• OutputDebugString
19. Servislerle ve Scheduled Task’larla
Kalıcılığı Sağlamak için Kullanılabilecek
API’ler
ŞÜPHELİ WINDOWS API'LERİ
İhtiyaç: Zararlının kalıcılık için servis veya scheduled task
kullanması durumunda kullandığı API’ler
• CreateService
• ControlService
• OpenSCManager
• NetScheduleJobAdd
20. ŞÜPHELİ WINDOWS API'LERİ
Diğer Dosyalara Bulaşmak için Kullanılan
API’ler
İhtiyaç: Zararlının diğer dosyalara da kod eklemek için kullandığı
API’ler
• FindFirstFile
• FindNextFile
• NtQueryDirectoryFile
• CreateFileMapping (Disk üzerindeki PE dosyalarına kod inject etmek için
bunları belleğe yükleyerek ona erişmek için kullanılan API)
• MapViewOfFile
21. ŞÜPHELİ WINDOWS API'LERİ
COM Nesnelerini Kullanmak için
Kullanılan API’ler
İhtiyaç: Zararlı yazılım Windows işletim sistemi ile gelen COM
bileşenlerini kullanabilir. Bu durumda hangi COM nesnesinin ve
dolayısıyla hangi fonksiyonalitenin kullanıldığını anlamak için onun
class identifier’ını (CLSID) incelememiz gerekir.
• OleInitialize (COM nesneleri kullanılmadan önce çağrılır)
• CoInitializeEx (COM nesneleri kullanılmadan önce çağrılır)
22. ŞÜPHELİ WINDOWS API'LERİ
Veri Sızdırmak, Parola Çalmak için
Kullanılabilecek API’ler
İhtiyaç: Zararlı yazılım sistem üzerinde tanımlı kullanıcıların parola hash’lerini
SAM veritabanına erişerek, sisteme erişmiş kullanıcıların parolalarını bellekteki
verilere erişerek ele geçirmek isteyebilir, sisteme map’lenmiş paylaşımlar içinde
de veri arayabilir.
• LsaEnumerateLogonSessions (Credential çalan zararlılar tarafından sisteme logon
olmuş kullanıcıları tespit etmek için kullanılabilir)
• SamIConnect (SAM veritabanına erişmek ve parola hash’lerini dump etmek için
kullanılır)
• SamIGetPrivateData (SAM veritabanına erişmek ve parola hash’lerini dump etmek
için kullanılır)
• SamQueryInformationUse (SAM veritabanına erişmek ve parola hash’lerini dump
etmek için kullanılır)
23. ŞÜPHELİ WINDOWS API'LERİ
Veri Sızdırmak, Parola Çalmak için
Kullanılabilecek API’ler (devamı)
• NetShareEnum (SAM veritabanına erişmek ve parola hash’lerini dump etmek
için kullanılır)
• ReadProcessMemory
• Toolhelp32ReadProcessMemory
24. ŞÜPHELİ WINDOWS API'LERİ
Diğer API’ler
• CreateMutex (Aynı anda zararlı prosesin tek bir instance’ının çalışması için
kullanılabilen bir API’dir. Mutex bir kilit gibi davranır ve bu kilide sahip olmayan proses
diğerlerini bekler)
• CreateProcess
• ShellExecute
• WinExec
• System (zararlı proses drop ettiği başka bir uygulamayı veya sistem üzerindeki başka
bir uygulamayı başlatmak için bu API’leri kullanabilir)
• CryptAcquireContext (Kriptolama fonksiyonları zararlı tarafından çeşitli amaçlarla
kullanılabilir, crypt ile başlayan pek çok fonksiyon incelenebilir)
• EnableExecuteProtectionSupport (DEP kontrolünü ortadan kaldırmak için
kullanılan fonksiyondur)
25. ŞÜPHELİ WINDOWS API'LERİ
Diğer API’ler (devamı)
• NtSetInformationProcess (Proses haklarını yükseltmek veya DEP kontrolünü
ortadan kaldırmak için kullanılabilir)
• GetSystemDefaultLangId (Ransomware v.b. mesaj veren zararlılar tarafından
sistemin dil ayarlarını tespit etmek için kullanılır)
• GetTempPath (zararlı yazılım bir takım geçici dosyaları saklamak için bu alanı
kullanabilir)
• SetFileTime (zararlı yazılım aktivitelerini gizleyebilmek için kullanabilir)
• StartServiceCtrlDispatcher (Servis olarak başlayan bir proses’in ilk 30 sn.
içinde bu fonksiyonu çağırması gerekir. Bu API’nin gözlenmesi zararlının servis olarak
çalıştırılması gerektiği anlamına gelebilir.)
• IsNTAdmin
• IsUserAnAdmin (zararlı kullanıcının admin haklarına sahip olup olmadığını kontrol
etmek için bu API’leri kullanabilir)
26. ŞÜPHELİ WINDOWS API'LERİ
ÖNEMLİ
• Derleyiciler bazı fonksiyonları yazılımcı bu fonksiyonu doğrudan
kullanmamış olsa da uygulamaya ekleyebilmektedir. Örneğin
[IsDebuggerPresent] fonksiyonunu aslında yazılımcı böyle
bir kontrol yapmasa da bazı uygulamalarda mevcut olarak
gözlemleyebilirsiniz.
• Şüpheli bir API’nin tam olarak nasıl kullanıldığını anlayabilmek
için disassembly üzerinde aldığı parametreleri incelemek ve
binary debugger ile dinamik analiz ile izlemek gerekecektir.
Bunu yapabilmek için de X86 mimarisi, X86 Assembly, calling
conventions (fonksiyon çağırma yöntemleri) bilgilerine sahip
olmak gereklidir.
27. ANALİZ
STATİK ANALİZ [Import Address Table]
Oluşturduğumuz şüpheli API
adları listesini kopyalayarak A ve
W uzantılı adlarını da türetelim
28. ANALİZ
STATİK ANALİZ [Import Address Table]
MS Excel'in VLOOKUP fonksiyonunu kullanarak
zararlı yazılımın IAT'ından elde ettiğimiz liste ile
şüpheli API listemizi karşılaştıralım
29. ANALİZ
STATİK ANALİZ [Import Address Table]
Henüz X86 mimarisi ile ilgili teorik bir çalışma
yapmadık, ancak küçük bir tanışıklık için ilgimizi
çeken bir API için [ör: RegSetValueExW]
zararlımızı inceleyelim.
Dinamik analiz sırasında zararlının Autorun
registry key'lerine değer yazdığını görmüştük,
zararlı yazılımın bu davranışını biraz daha
yakından inceleyelim.
30. ANALİZ
STATİK ANALİZ [Import Address Table]
RegSetValueExW fonksiyonunun çağrıldığı yerleri
bulmak ve bu bölümleri inceleyebilmek için IDA
Pro'nun Imports penceresinden faydalanabiliriz.
Bunun için bu fonksiyonun üzerinde çift tıklayalım.
31. ANALİZ
STATİK ANALİZ [Import Address Table]
Buradan RegSetValueExW fonksiyonunun IAT'daki
yerine geliriz. Fonksiyon adına tıkladıktan sonra "X"
harfine basarsak IDA bize bu adresin kullanıldığı kod
bölümlerinin Cross (bu yüzden X) Reference
bilgilerini verir.
32. ANALİZ
STATİK ANALİZ [Import Address Table]
XREF listesinde seçtiğimiz bir satıra çift
tıkladığımızda ilgili kod için disassembly ekranına
geçeriz. Mouse'umuzu fonksiyon adı üzerine
getirdiğimizde fonksiyonun parametre yapısını
görebiliriz.
X86 hakkında daha detaylı çalışacağız, ancak fonksiyonların parametrelerini Stack'e yazılan
değerlerden aldığını söyleyelim. Disassembly üzerinden bu değerlerin neler olduğunu
anlamak güç, çünkü parametrelerin bir kısmı daha yukarıda yapılan hesaplamalar ve diğer
işlemler sonucunda belirlenerek stack'e yazılıyor (push ediliyor).
33. ANALİZ
STATİK ANALİZ [Import Address Table]
Windows API'leri ve bu
fonksiyonların alabilecekleri
parametre değerleri ile ilgil
detaylı bilgiyi MSDN v.d.
kaynaklardan bulabilirsiniz.
34. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
RegSetValueExW fonksiyonuna
verilen parametreleri statik
olarak analiz etmek zor olduğu
için Immunity Debugger ile
dinamik analiz yapalım ve bu
fonksiyon çağrılmadan önce
Stack'te bulunacak parametreleri
inceleyelim.
35. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Uygulamanın ASLR destekli olması nedeniyle unpack edilmiş olan uygulamada da hata
alıyoruz [uzun incelemeler sonrasında ulaştığım bu sonuç hakkında bana güvenin!]
Ancak bu hata yüzünden olmasa da yine de ASLR desteğini kaldırmamız gerekecekti,
yoksa IDA'da öğrendiğimiz adresi bellekte aynı adreste bulamayacaktık
36. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Unpack edilmiş zararlı yazılımımıza PEView ile göz attığımızda Image Optional Header içinde
bulunan DLL Characteristics alanında ASLR desteği bulunduğunu görebiliriz [Dynamic Base
özelliği]
OS Loader bu özelliği dikkate alarak uygulamayı ASLR ile belleğe yükler. Bundan kaçınmak için
uygulamanın ASLR'ı desteklemediğini belirtmemiz lazım. Bu alanın dosya içindeki offset'inin
0x176 byte'ta olduğunu PEView'ın pFile adres formatı ile görebiliriz.
37. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Uygulama dosyasını yamalamak için
010 Editör'le dosyayı açmadan önce
Immunity Debugger ve PEView'ı
kapatalım, aksi takdirde dosya Read
Only olarak açılacaktır
DLL Characteristics
alanının mevcut değeri
40 81 (PEView'da little
endian formatta
gösterilmiştir)
39. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Yamamızdan sonra unpacked
zararlı yazılım uygulamamızın
ASLR (Dynamic Base) desteğinin
kalktığını görebiliriz
40. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Uygulamamızı bu defa sorunsuz başlatabiliriz.
RegSetValueExW fonksiyonunun tam
çağrıldığı noktada breakpoint koymak için [b
40298f] komutunu kullanabiliriz.
41. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Imminuty Debugger'ın [b] düğmesiyle erişilen
Breakpoints ekranına göz attığımızda
breakpoint'imizin başarı ile konup
konmadığını gözleyebiliriz.
42. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Normalde çalışan
uygulamamız Immunity
Debugger ile çalıştırdığımızda
[Division by zero] hatası aldı
Shift+F9 ile kontrolü
uygulamaya iade ettiğimizde
de herhangi bir ilerleme
gözlemleyemedik
EAX register'ının hata
anındaki değeri [0]
44. DİNAMİK ANALİZ [Import Address Table]
ANALİZ
Hata aldığımız noktanın öncesini
Immunity Debugger'ın
Disassembly penceresinden de
inceleyebiliriz, ama incelememizi
IDA'da yapmak istersek [Jump to
address] aracılığıyla ilgili adrese
gidebiliriz
46. DİNAMİK ANALİZ [Import Address Table]
ANALİZ
[div eax] instruction'ından hemen önceki satıra bakarsak [xor eax, eax]
instruction'ı ile EAX register'ının değerinin 0'landığını görebiliriz. Bu bilgi
0'a bölme hatasının kasten oluşturulduğu şüphesini doğuruyor. Bu
satırdan hemen önce de yeni bir Exception Handler belirlenmiş.
Uygulama çalışırken bu handler hatayı ele alıyor olmalı, ancak Immunity
kontrolü uygulamaya devrettiğinde aynı durum gerçekleşmiyor.
47. HATA ALDIRMA ÖRNEĞİ
ANTI-DEBUGGING YÖNTEMLERİ
ImmunityDebugger'ın Options /
Debugging options / Exceptions
penceresinde istediğimiz hata
kodları için kontrolün otomatik
olarak uygulamaya devredilmesi
ayarını yapabiliriz
48. HATA ALDIRMA ÖRNEĞİ
ANTI-DEBUGGING YÖNTEMLERİ
Tam hata kodunu öğrenmek için bu
ekrandaki "Add last exception"
düğmesini kullanabiliriz, veya bir
aralık tanımlayarak 0 – FFFFFFFF
aralığındaki tüm exception'ları göz
ardı ettirebiliriz.
49. HATA ALDIRMA ÖRNEĞİ
ANTI-DEBUGGING YÖNTEMLERİ
ImmunityDebugger'ın bu özelliğini
kullanarak Exception'ı uygulamaya
aktarma denememiz başarılı olamadı.
50. ANTI-DEBUGGING YÖNTEMLERİ
AMAÇ
• Anti-Debugging yöntemleri ile zararlı yazılım geliştiren taraf
dinamik analizi zorlaştırmak ister.
• Ancak hemen her durumda bu kontrolün tespiti ve uygulama
yamalanarak (debug kontrolü yapan instruction'ların NOP
instruction'ları ile etkisiz hale getirilmesiyle) atlatılması
mümkündür.
• Bu kontrollerin uygulandığı noktalar tespit edildikten sonra
dinamik analiz sırasında bu noktalarda registry değerleri veya
belleğe manuel müdahale etme veya bu debugger'ların
sağladığı bazı atlatma imkanlarının kullanılması da anti-
debugging engellerini atlatmak için kullanılabilir.
51. ANTI-DEBUGGING YÖNTEMLERİ
ÖRNEKLER
API Metodları
• [IsDebuggerPresent()] – PEB bloğundaki BeingDebugged byte'ının
değerini kontrol eder
• [CheckRemoteDebuggerPresent(GetCurrentProcess(),
&isDebuggerPresent)] – Mevcut proses'imizin handle'ını kullarak debug
edilip edilmediğini test eder
• [NtQueryInformationProcess(...)] – Debug Flag'lerini verilen
değişken referansına döndürür
• [OutputDebugString(L"test")/GetLastError()] – proses debug
edilmiyorsa GetLastError ile dönen hata kodu değişecektir
• [DebugActiveProcess(pid)] – Eğer proses zaten debug ediliyorsa hata
döndürecektir
52. ANTI-DEBUGGING YÖNTEMLERİ
ÖRNEKLER
Manuel Kontrol Yöntemleri
• Debugger uygulama varlığını arama:
[FindWindow(L"OLLYDBG",0)] fonksiyonu ile aktif uygulamaların
adlarını inceleme, registry 'yi veya dosya sistemini debugger
varlığını keşfetmek için tarama
• Manuel olarak PEB bloğu içindeki BeingDebugged değerini
arama
53. ANTI-DEBUGGING YÖNTEMLERİ
ÖRNEKLER
Breakpoint Arama ve Kod Bütünlüğünü Test Etme
• Software breakpoint ve yama tespiti: Belli bir kod bölümünün
byte'larını basit bir biçimde bir byte'lık bir değişkene ekleyerek
hesaplanan değer önceden hesaplanmış bir değerle
karşılaştırılabilir. Veya her bir byte bir değerle XOR'lanarak 0xCC
olup olmadığı test edilebilir (ör: [0xCC XOR 0x55 == 0x99])
• Hardware breakpoint tespiti: [GetThreadContext(hnd, &ctx)]
fonksiyonuyla DR0, DR1, DR2, DR3 register değerlerini içeren
context veri yapısı incelenebilir.
54. ANTI-DEBUGGING YÖNTEMLERİ
ÖRNEKLER
Zaman Aralıklarını Ölçme
• [GetTickCount() ], [timeGetTime()] gibi fonksiyonlarla art arda
ölçülen zaman değerleri arasındaki fark test edilerek proses'in
debug edilip edilmediği anlaşılmaya çalışılır.
55. ANTI-DEBUGGING YÖNTEMLERİ
UYGULAMANIN YAMALANMASI
Debug etmemizi engelleyen bu durumu aşmak için hata alan satırı NOP
instruction'ı ile yamalayacağız.
Bunun için önce Options / IDA Options / Disassembly menüsünde "Number of
opcode bytes" değerini 8 yaparak Opcode'ların görünür olmasını sağlayalım.
58. ANTI-DEBUGGING YÖNTEMLERİ
UYGULAMANIN YAMALANMASI
IDA Pro'nun güncel versiyonunda disk
üzerindeki dosyanın yamalanması imkanı var.
Ne yazık ki ücretsiz versiyon olan IDA Pro 5'te
"Patch program" seçeneği bulunmuyor.
59. ANTI-DEBUGGING YÖNTEMLERİ
UYGULAMANIN YAMALANMASI
Uygulamayı IDA'da yamalayamadığımız için VA ve
dosya offset dönüşümünü PEView ile yapmaya
çalışacağız. Bunun için önce VA adresi olarak
0x401D8A adresinin bulunduğu yeri bulalım.
Yamalanacak olan [F7 F0] byte'ları işaretlenen satırın
11. ve 12. byte'ları.
67. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Uygulama sonlandı ancak
registry değerinin atandığı
instruction çalışmadı. Böyle bir
durum zararlının daha önce bu
sistem üzerinde çalışmış olması
nedeniyle tekrar sisteme
bulaşmamak istemesinden
kaynaklanmış olabilir. Bu
nedenle infection öncesi
SnapShot'a dönerek
uygulamayı tekrar denemekte
fayda var.
68. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Kurban sistemi kurulumlar
sonrası aldığımız snapshot'a
döndürdüğümüzde üzerinde
çalıştığımız ve yamaladığımız
zararlı kopyalarını da
kaybedeceğiz. Bunun için
snapshot'a dönmeden önce
yamayı uyguladığımız
fatura_pdf-decompress.exe
dosyasını Host bilgisayarımıza
kopyalayalım.
70. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Kurban sistemimiz kurulumlar
sonrasında henüz zararlı
yazılımı çalıştırmadan önceki
haline dönecektir.
71. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Host bilgisayarımıza aldığımız Anti-Debug
amaçlı kod bölümünü yamaladığımız
"fatura_pdf-decompress.exe" dosyasını
Host bilgisayardan kopyalayalım.
73. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
"fatura_pdf-decompress.exe"
dosyasını Immunity Debugger
ile başlatalım. Başlatır
başlatmaz da 0x40298F
adresine breakpoint koyalım.
74. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Uygulamayı devam ettirdiğimizde PDF
dosyasının görüntülendiğini
görüyoruz. Bu davranış son
denememizden farklı, muhtemelen
uygulama daha önceden bulaşmadığı
bir sistem üzerinde bu şekilde
davranıyor. Uygulama tanımladığımız
breakpoint adresinde durdu.
75. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
RegSetValueExW fonksiyonu
çağrılmadan hemen önce Stack'te bu
fonksiyona verilen parametreleri
görebiliriz. Bunlardan "ValueName"
parametresi Registry değerini ifade
ediyor
76. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
MSDN'den edindiğimiz bilgiye göre RegSetValueEx fonksiyonuna verilen 5.
parametre Registry değerine atanacak veriye işaret eden bir pointer
77. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
ImmunityDebugger'ın Buffer olarak adlandırdığı
bu alanda belirtilen adrese giderek atanan veriyi
gözlemlemek için sol alt bölümdeki Memory
Dump alanında sağ klikleyerek "Go to" /
"Expression" seçeneğini seçelim.
79. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Bu adreste bulunan Unicode
string'i biraz daha rahat
gözlemleyebilmek için dump
alanında sağ klikleyerek
"Hex / UNICODE (16 bytes)"
seçeneğini işaretleyelim.
80. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
Bu alanda bir exe dosya adı
görüyoruz.
SORU: Burada gözlemlediğimiz dosya adını daha
önce nerede gözlemlemiştik?
81. ANALİZ
DİNAMİK ANALİZ [Import Address Table]
hKey handle'ının hangi registry key'ine
ait olduğunu anlamak için daha
yukarıda çağrılan RegOpenKeyExW
fonksiyonunun çağrıldığı yere
breakpoint koymamız lazım.
Tabi bunun için sanal makinemizi yine
kurulumlar sonrası snapshot'ımıza
döndürmeyi unutmamalıyız.
82. EGZERSİZ
DİNAMİK ANALİZ [Import Address Table]
• Import Address Table'da ilginç bulduğunuz fonksiyonlar için
Immunity Debugger ile benzer analizleri yapınız.
• Fonksiyonun çağrıldığı adrese breakpoint koyunuz ve stack'te
fonksiyon için hazırlanmış olan parametreleri inceleyiniz.
Parametreler hakkında detaylı bilgi için MSDN'e başvurunuz.
• Tespitlerinizi bu noktaya kadar yapmış olduğumuz strings
analizleri ve capturebat ile yaptığımız dinamik analiz sonuçları
ile birlikte değerlendiriniz.
• Daha ileri statik ve dinamik analiz yapabilmek için gerekli X86
temel bilgileri üzerinde sonraki bölümde durulacaktır.
83. X86 MİMARİSİNE GİRİŞ
İNCELEYECEĞİMİZ UYGULAMA
PE dosya formatını da inceleme için
kullandığımız bu basit C
uygulamasından derlenmiş kodu
Immunity Debugger üzerinde
incelerken temel X86 bilgilerine
değineceğiz.
84. X86 MİMARİSİNE GİRİŞ
ANA KONULAR VE KAVRAMLAR
Daha Önce Değindiklerimiz
• Derleme ve linkleme
• OS Loader'ın uygulamayı belleğe yüklemesi
• Executable imajı, DLL modülleri, Stack ve Heap
alanlarının bellekteki konumları [ASLR konusuyla
bağlantıları]
• Virtual Address kavramı
85. X86 MİMARİSİNE GİRİŞ
ANA KONULAR VE KAVRAMLAR (DEVAMI)
Bu Bölümde Değineceklerimiz
• Register'lar ve kullanım amaçları
• X86 instruction set'i
• Stack alanının kullanımı [fonksiyon çağırma, fonksiyon
içindeki ve fonksiyondan çıkış işlemleri sırasındaki
olaylar]
• Calling conventions [fonksiyon çağırma sırasında
parametrelerin fonksiyona aktarım yöntemleri,
fonksiyon dönüşünde stack'i temizleme görevleri]
86. X86 MİMARİSİNE GİRİŞ
X86 hakkındaki teorik bilgiler
üzerinde daha sonrak konuşmak
üzere, öncelikle uygulamamızın
Makine Kodu seviyesindeki
işleyişini inceleyelim.
Öncelikle main() fonksiyonu
içindeki ilk instruction'ın adresini
öğrenmek için Visual Studio'nun
"Go To Assembly" özelliğini
kullanacağız.
87. X86 MİMARİSİNE GİRİŞ
Visual Studio'da Disassembly view'ına
geçtiğimizde derleyicinin üreteceği
Assembly kodlarının yanı sıra bunların
adreslerini de görüyoruz.
Bu bilgiye şu nedenle ihtiyacımız var:
Derleyiciler kod üretmeye doğrudan
main() fonksiyonundan başlamazlar ve
Address of Entry Point'te main()
fonksiyonuna işaret etmez. Ancak bizim
X86 mimarisini anlama amaçlı
incelememizi main() fonksiyonundan
başlatmamız lazım.
İlk instruction olan "push ebp"
instruction'ının adresi 0x401060
88. X86 MİMARİSİNE GİRİŞ
Uygulama belleğe yüklendiğinde de
bu adreste olacağından emin
olabilmek için ASLR'ı bu ayarlarla
etkisiz hale getirdik.
89. X86 MİMARİSİNE GİRİŞ
Address of Entry Point'e koyduğumuz
breakpoint [0x401060] sonrası bir kaç
defa F9 tuşuna basılarak main()
fonksiyonunun başına geliyoruz.
90. X86 MİMARİSİNE GİRİŞ
main() fonksiyonunun ilk instruction'ları
"function prologue" adı da verilen önceki
fonksiyon frame pointer'ın saklanması ve
mevcut stack pointer'ın yeni fonksiyonun
frame pointer'ı olarak belirlenmesidir.
91. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
STACK
DEĞİŞEN
REGISTER'LAR
ESP 0x0018FF40
ESP
Yüksek Adres
Düşük Adres
Buradaki stack gösterimimiz ile Immunity
Debugger'ın stack penceresindeki büyüme
yönleri farklı, bu nedenle biraz kafanız
karışabilir !
92. X86 MİMARİSİNE GİRİŞ
"function prologue"un ikinci adımında
mevcut stack pointer [ESP] main()
fonksiyonunun stack base pointer'ı [EBP]
olarak atanıyor.
93. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
STACK EBP 0x0018FF40
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
94. X86 MİMARİSİNE GİRİŞ
main() fonksiyonunun lokal değişkenleri için
stack'te 12 byte'lık [0x0C] yer ayrılıyor.
95. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
XXXXXXXX
XXXXXXXX
XXXXXXXX
STACK ESP 0x0018FF34
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
97. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
XXXXXXXX
0x0000000A
XXXXXXXX
STACK ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
99. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
STACK ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
101. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
STACK ESP
EAX 0x00000014
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
102. X86 MİMARİSİNE GİRİŞ
EAX register'ının değeri Stack'e push
ediliyor. Bu değer "add" fonksiyonu için bir
parametre olarak kullanılacak.
103. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]STACK
ESP
ESP 0x0018FF30
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
105. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]STACK
ESP
ECX 0x0000000A
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
106. X86 MİMARİSİNE GİRİŞ
ECX register'ının değeri Stack'e push
ediliyor. Bu değer "add" fonksiyonu için bir
diğer parametre olarak kullanılacak.
107. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]
0x0000000A [Arg 1]
STACK
ESP
ESP 0x0018FF2C
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
108. X86 MİMARİSİNE GİRİŞ
Immunity Debugger debug sembollerine
sahip olduğu bir executable için bize
fonksiyon adı ve bu fonksiyona verilen
parametrelerle ilgili bilgi sağlıyor.
109. X86 MİMARİSİNE GİRİŞ
"call add" instruction'ı çalıştığında EIP
değeri add fonksiyonuna işaret edecek
şekilde değişiyor, ancak main() fonksiyonu
içindeki bir sonraki instruction'ın adresi
stack'e yazılıyor.
110. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
STACK
ESP
ESP 0x0018FF28
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
111. X86 MİMARİSİNE GİRİŞ
add() fonksiyonunun prologue'unın ilk
instruction'ı çalıştığında main()
fonksiyonunun stack base pointer'ı stack'e
yazılıyor.
112. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
STACK
ESP
ESP 0x0018FF24
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
113. X86 MİMARİSİNE GİRİŞ
add() fonksiyonunun prologue'unın ikinci
instruction'ı çalıştığında ESP register'ının
değeri add() fonksiyonunun stack base
pointer'ına [EBP] yazılıyor.
114. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
STACK
ESP
EBP 0x0018FF24
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
115. X86 MİMARİSİNE GİRİŞ
add() fonksiyonu lokal değişkenler için
sadece 4 byte'lık bir alan kullandığından
"push ecx" instruction'ı ile stack'te yeterli
alan ayrılmış oluyor.
116. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
XXXXXXXX
STACK
ESP
ESP 0x0018FF20
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
117. X86 MİMARİSİNE GİRİŞ
add() fonksiyonunun stack'te hazır bulunan
birinci parametresi [EAX] register'ına
atanıyor.
118. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
XXXXXXXX
STACK
ESP
EAX 0x0000000A
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
119. X86 MİMARİSİNE GİRİŞ
add() fonksiyonunun stack'te hazır bulunan
ikinci parametresi [EAX] register'ına [add]
instruction'ı ile ekleniyor.
120. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
XXXXXXXX
STACK
ESP
EAX 0x0000001E
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
121. X86 MİMARİSİNE GİRİŞ
[EAX] register'ının değeri stack'te add()
fonksiyonu için ayrılmış 4 byte'lık lokal
değişken alanına atanıyor. Uygulamamızı
optimizasyon ayarları ile derlemiş olsaydık
muhtemelen bu adımı görmeyecektik. Çünkü
zaten return değeri [EAX] register'ı ile
döndürülecek.
122. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
123. X86 MİMARİSİNE GİRİŞ
Bu adımda gereksiz bir biçimde lokal
değişken tekrar [EAX] register'ına atanıyor.
Ancak register değeri zaten aynı olduğu için
değişmiyor.
124. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
125. X86 MİMARİSİNE GİRİŞ
add() fonksiyonun epilogue'unun ilk
adımında add() fonksiyonunun stack base
pointer'ı [ESP] register'ına atanıyor. Bu
adımda add() fonksiyonu için stack'ten
almış olduğumuz alan geri veriliyor.
126. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK ESP 0x0018FF24
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
127. X86 MİMARİSİNE GİRİŞ
add() fonksiyonun epilogue'unun ikinci
adımında main() fonksiyonunun stack'te
saklanmış olan [EBP] register değeri tekrar
yükleniyor. pop instruction'ı ile birlikte stack
de 4 byte daha azaltılmış oluyor.
128. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK EBP 0x0018FF40
ESP 0x0018FF28
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
129. X86 MİMARİSİNE GİRİŞ
"retn" instruction'ı çalıştığında stack'in en
üstünde bulunan saklanmış [EIP] değeri
"pop" edilecek ve uygulama akışı bu
adresten devam edecektir.
130. X86 MİMARİSİNE GİRİŞ
"retn" instruction'ı çalıştığında sadece
saklanmış EIP değerine atlamıyoruz aynı
zamanda stack'de 4 byte küçültülmüş
oluyor.
131. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK ESP 0x0018FF2C
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
132. X86 MİMARİSİNE GİRİŞ
"add esp, 8" instruction'ı ile stack'te add()
fonksiyon parametreleri için kullanmış
olduğumuz 8 byte'lık alanı geri veriyoruz. Bu
işlemde çağıran [caller] stack'i temizliyor.
133. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
XXXXXXXX
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK ESP 0x0018FF34
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
134. X86 MİMARİSİNE GİRİŞ
[EAX] register'ında bulunan toplama işlemi
sonucu main() fonksiyonunun lokal
değişkenine atanıyor.
135. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
0x0000001E
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
137. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
0x0000001E
0x00000014 [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK EDX 0x0000001E
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
138. X86 MİMARİSİNE GİRİŞ
Optimizasyon uygulamadığımız için printf()
fonksiyonunun ikinci parametresi olarak
(parametreler yine sağdan sola doğru
stack'e yazılıyor) [EDX] register'ında
bulunan toplam değerini stack'e yazıyoruz
(halbuki bu değer zaten stack'te mevcuttu)
139. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
0x0000001E
0x0000001E [Arg 2]
0x0000000A [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK ESP 0x0018FF30
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
Buradaki Arg 2 printf()'in ikinci
parametresi
140. X86 MİMARİSİNE GİRİŞ
Bu instruction'da printf() fonksiyonu için
format string'in bulunduğu adresi stack'e
yazıyoruz. Stack penceresinde bu değerin
Sonuc = "%d" olduğunu görebiliriz.
141. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
0x0000001E
0x0000001E [Arg 2]
0x00403000 [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK ESP 0x0018FF2C
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
142. X86 MİMARİSİNE GİRİŞ
printf() fonksiyonu çağrılmadan önce
konsol penceresini görünteleyelim ve
henüz birşey yazılmadığını görelim.
143. X86 MİMARİSİNE GİRİŞ
printf fonksiyonunun içine girmeden "step over" şeklinde bu
fonksiyonu çalıştırıyoruz, çünkü bu API fonksiyonunu
incelemek istemiyoruz. Bu çağrıdan sonra Sonuc = 30
ifadesini görmemizin yanında ilginç olan bir nokta [EAX],
[ECX] ve [EDX] register'larımızın değişmiş olması.
144. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
0x0000001E
0x0000001E [Arg 2]
0x00403000 [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK EAX 0x0018FF2C
ECX 0x0018FF2C
EDX 0x0018FF2CESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
"caller saved" registers kavramına
"calling conventions" bölümünde
değineceğiz.
146. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
0x0000001E
0x0000001E [Arg 2]
0x00403000 [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK ESP 0x0018FF34
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
147. X86 MİMARİSİNE GİRİŞ
getchar() API fonksiyonunun
çalışabilmesi için konsol penceresinden
bir girdi vermemiz gerekecek. Bu
yüzden "step over" komutu ile bir
instruction daha atlamaya çalıştıktan
sonra konsolu tekrar ön plana alalım.
148. X86 MİMARİSİNE GİRİŞ
Konsol penceresinde "Enter" tuşuna
bastıktan sonra getchar() fonksiyonu
tamamlanarak dönüyor. [ECX] ve [EDX]
register'larının değiştiğini gözlemliyoruz.
149. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
0x0000001E
0x0000001E [Arg 2]
0x00403000 [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK ECX 0x683D60B0
EDX 0x0000000A
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
150. X86 MİMARİSİNE GİRİŞ
main() fonksiyonundan çıkmadan önce
[EAX] register'ı return değeri olarak
"0"lanmak amacıyla kendisiyle XOR'lanıyor.
151. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
0x0000001E
0x0000001E [Arg 2]
0x00403000 [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK EAX 0x00000000
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
152. X86 MİMARİSİNE GİRİŞ
main() fonksiyonundan çıkmadan önce
epilogue adımlarından main() fonksiyonu
için alınmış olan stack alanını geri veren
"mov esp, ebp" instruction'ı çalıştırılıyor.
153. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
0x0000001E
0x0000001E [Arg 2]
0x00403000 [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK ESP 0x0018FF40
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
154. X86 MİMARİSİNE GİRİŞ
main() fonksiyonu 2. epilogue adımında
main() fonksiyonunu çağıran bir üst
fonksiyonun [EBP] register'ı restore ediliyor.
155. X86 MİMARİSİNE GİRİŞ
Stack ve Registry Değişim Özeti
0x0018FF88 [EBP]
0x00000014
0x0000000A
0x0000001E
0x0000001E [Arg 2]
0x00403000 [Arg 1]
0x00401081 [EIP]
0x0018FF40 [EBP]
0x0000001E
STACK EBP 0x0018FF88
ESP 0x0018FF44
ESP
DEĞİŞEN
REGISTER'LAR
Yüksek Adres
Düşük Adres
156. X86 MİMARİSİNE GİRİŞ
main() fonksiyonunun sonunda "retn"
instruction'ı çalıştığında saklanmış olan
[EIP] adresinden uygulama akışı devam
ediyor.
158. X86 MİMARİSİNE GİRİŞ
İNCELEYEDİĞİMİZ UYGULAMA
Assembly kodlarının üzerinden
geçtikten sonra kaynak kodumuzu
tekrar hatırlamak isteyebilirsiniz.
(Aslında her aşamada bu kaynak koda
göz atmak disassembly'yi anlamak için
size yardımcı olabilir.)
159. X86 MİMARİSİNE GİRİŞ
REGISTER'LAR VE KULLANIM AMAÇLARI
EIP [Extended Instruction Pointer]
Bir sonra çalışacak olan instruction’ın adresi (sadece makine tarafından değiştirilir)
EBP [Extended Base Pointer] ve ESP [Extended Stack Pointer]
İçinde bulunulan fonksiyonun stack frame’inin taban ve tavan adreslerini
barındıran register’lar
EAX, EBX, ECX, EDX, ESI, EDI
Genel amaçlı register’lar (ancak geleneksel olarak belli amaçlarla kullanılabilirler)
EFLAGS
Çeşitli instruction’lar tarafından etkilenen ve kullanılan bayrakları barındıran 32
bit’lik bir register
160. X86 MİMARİSİNE GİRİŞ
REGISTER'LAR VE KULLANIM AMAÇLARI
• EAX – Accumulator Register: Genellikle fonsiyonların return değerlerini tutar.
Hesaplamaların sonuçları da genellikle bu register'da tutulur.
• EBX – Base Register: Genellikle belli bir özel amaç için kullanılmaz.
• ECX – Counter Register: Genellikle döngülerde ve string işlemlerinde sayaç
değişkenini (yani meşhur "i" değerini) tutmak için kullanılır.
• EDX – Data Register: Çarpma ve bölme gibi işlemlerde EAX'in yetersiz kalması
nedeniyle işlem sonuçlarının most significant bit'lerini tutmak için kullanılır.
• ESI – Source Index: Genellikle string yazma işlemlerinde kopyalanacak string'den
okunacak adresi tutar (read pointer).
• EDI – Destination Index: Genellikle string yazma işlemlerinde yazılacak adresi tutar
(write pointer).
Önemli: ESP ve EBP register'ları ile birlikte yukarıdaki genel amaçlı register'lar assembly
yazan uygulama geliştirici veya derleyici tarafından istenildiği gibi kullanılabilir. Ancak
yukarıda belirtilen kullanımlar geleneksel kullanımlardır.
161. X86 MİMARİSİNE GİRİŞ
REGISTER'LAR VE KULLANIM AMAÇLARI
EAX
EBX
ECX
EDX
AX
BX
CX
DX
078151631
32 Bit
Register'lar
16 Bit
Register'lar
8 Bit
Register'lar
Bu bölümleme sadece EAX,
EBX, ECX ve EDX'e özeldir
163. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Bellek adresleme yöntemleri
Instruction Anlamı
mov eax, [ebx] EBX register'ının içerdiği adresteki 4 byte'lık değeri EAX
register'ına atama
mov [var], ebx EBX register'ının değerini 32bit'lik bir değişkenin içerdiği adrese
yazma
mov eax, [esi-4] ESI + (-4) adresindeki 4 byte'lık değeri EAX register'ına atama
mov [esi+eax], cl CL register'ının taşıdığı bir byte'lık değeri ESI+EAX adresine
yazma
mov edx, [esi+4*ebx] ESI+4*EBX adresinde bulunan 4 byte'lık değeri EDX register'ına
atama
164. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Size Directive'leri
Register adlarının geçtiği instruction'lardan okunacak ve/veya yazılacak verinin
büyüklüğü anlaşılabilir. Ancak immediate (sabit) verilerin yazıldığı durumlarda
yazılacak verinin kaplayacağı alanın belirtilmesi gerekir.
Instruction Anlamı
mov BYTE PTR [ebx], 2 2 sabit değerini EBX register'ında saklı adresteki 1 byte'lık
alan yaz
mov WORD PTR [ebx], 2 2 sabit değerinin 16 bit'lik gösterimini EBX register'ında saklı
adresten başlayan 2 byte'lık alana yaz
mov DWORD PTR [ebx], 2 2 sabit değerinin 32 bit'lik gösterimini EBX register'ında saklı
adresten başlayan 2 byte'lık alana yaz
165. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar
Şu ana kadar pek çok assembly instruction'ı ile karşılaştık. Açıkçası belli bir
instruction set'i kullanılan veya derleyicilerin ürettiği assembly kodlarının önemli
bir bölümünü kapsar.
Bu bölümde daha önce de karşılaştığımız çok kullanılan Assembly
Instruction'larının üzerinden geçeceğiz.
166. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Data Movement Instruction'ları]
mov
Move (Opcodes: 88, 89, 8A, 8B, 8C, 8E, ...)
Bellekten belleğe mov instruction'ı ile veri kopyalamak mümkün değildir.
Syntax
mov <reg>,<reg>
mov <reg>,<mem>
mov <mem>,<reg>
mov <reg>,<imm>
mov <mem>,<imm>
167. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Data Movement Instruction'ları]
push
Push stack (Opcodes: FF, 89, 8A, 8B, 8C, 8E, ...)
Syntax
push <reg32>
push <mem>
push <imm32>
168. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Data Movement Instruction'ları]
pop
Pop stack
Syntax
pop <reg32>
pop <mem>
169. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Data Movement Instruction'ları]
lea
Load effective address
Bu instruction diğerlerine nazaran biraz kafa karıştırıcı olabilir. "lea"
instruction'ın da kaynak değer mutlaka köşeli parantezler arasındadır, ancak
"mov" instruction'ı için bu köşeli parantezler arasında adresi bulunan bellek
alanındaki değer anlamına gelirken "lea" instruction'ı doğrudan adres değerini
kullanır. Bu imkan zaman zaman adres bilgisini kullanmaktan ziyade hesaplama
amacıyla kullanılmaktadır.
Örnek
lea edi, [ebx+4*esi] - EBX+4*ESI hesaplamasının sonucu EDI register'ına atanır
170. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Aritmetik ve Mantık Instruction'ları]
add
Integer Addition
Toplama instruction'ında en fazla bir operand bellek alanı olabilir.
Syntax
add <reg>,<reg>
add <reg>,<mem>
add <mem>,<reg>
add <reg>,<imm>
add <mem>,<imm>
171. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Aritmetik ve Mantık Instruction'ları]
sub
Integer Subtraction
Toplama instruction'ında olduğu gibi çıkarma instruction'ında da en fazla bir
operand bellek alanı olabilir.
Syntax
sub <reg>,<reg>
sub <reg>,<mem>
sub <mem>,<reg>
sub <reg>,<imm>
sub <mem>,<imm>
172. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Aritmetik ve Mantık Instruction'ları]
inc, dec
Increment, Decrement
Artırma instruction'ı registry veya bellek alanını bir artırırken azaltma
instruction'ı da tam tersini gerçekleştirir.
Syntax
inc <reg>
inc <mem>
dec <reg>
dec <mem>
173. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Aritmetik ve Mantık Instruction'ları]
imul
Integer Multiplication
2 veya 3 operand'la çalışır. 2 operand'lı kullanımda iki değer çarpılıp sonuç
birinci operand'da saklanır. 3 operand'lı kullanımda 2. ve 3. operand'lar çarpılıp
sonuç birinci operand'da saklanır. Birinci operand (destination) her zaman
registry olmak zorundadır, 3 operand'lı kullanımda 3. operand her zaman bir
immediate (sabit) değer olmak zorundadır.
Syntax
imul <reg32>,<reg32>
imul <reg32>,<mem>
imul <reg32>,<reg32>,<imm>
imul <reg32>,<mem>,<imm>
174. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Aritmetik ve Mantık Instruction'ları]
idiv
Integer Division
EDX:EAX registerlarında bulunan 64 bit'lik integer değeri operand değerine
böler. Bölüm EAX'e kalan ise EDX register'ına yazılır.
Syntax
idiv <reg32>
idiv <mem>
175. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Aritmetik ve Mantık Instruction'ları]
and, or, xor
Bitwise logical and, or and exclusive or
Operand'lar üzerinde belirtilen işlemleri gerçekleştirdikten sonra sonucu birinci
operand'a yazarlar.
Syntax
and <reg>,<reg>
and <reg>,<mem>
and <mem>,<reg>
and <reg>,<imm>
and <mem>,<imm>
or <reg>,<reg>
or <reg>,<mem>
or <mem>,<reg>
or <reg>,<imm>
or <mem>,<imm>
xor <reg>,<reg>
xor <reg>,<mem>
xor <mem>,<reg>
xor <reg>,<imm>
xor <mem>,<imm>
176. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Aritmetik ve Mantık Instruction'ları]
shl, shr
Shift Left, Shift Right
Birinci operand'ı sola veya sağa kaydırırken boşalan bit pozisyonlarına 0 yazılır.
Kaç bit kaydırma yapılacağı 8 bit'lik bir immediate değerde veya CL register'ı ile
verilir.
Syntax
shl <reg>,<imm8>
shl <mem>,<imm8>
shl <reg>,<cl>
shl <mem>,<cl>
shr <reg>,<imm8>
shr <mem>,<imm8>
shr <reg>,<cl>
shr <mem>,<cl>
177. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Akış Kontrol Instruction'ları]
jmp
Jump
Uygulama akışını koşulsuz olarak operand ile belirtilen adrese yönlendirir.
Syntax
jmp <label>
178. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Akış Kontrol Instruction'ları]
jcondition (Conditional Jump)
EFLAGS register'ında tutulan ve son aritmetik işlemin (bunlar add, sub gibi instruction'lar olabileceği
gibi cmp ve test gibi çıkarma işleminin türevleri olan karşılaştırma instruction'ları da olabilir)
sonuçlarına bağlı olarak uygulama akışını operand'da belirtilen adrese yönlendirir veya akışın bir
sonraki instruction'dan devam etmesine izin verir. ZF - zero flag yapılan işlem veya karşılaştırmanın
sonucu "0" ise "1" değerini alır. SF - sign flag yapılan işlemin sonucu negatif ise "1" değerini alır.
Syntax
je <label> (jump when equal)
jne <label> (jump when not equal)
jz <label> (jump when last result was zero)
jg <label> (jump when greater than)
jge <label> (jump when greater than or equal to)
jl <label> (jump when less than)
jle <label> (jump when less than or equal to)
179. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Akış Kontrol Instruction'ları]
cmp
Compare
CMP instruction'ı SUB instruction'ına benzer ancak çıkartma işleminin sonucu
birinci operand'a yazılmaz, sadece işlemin sonucu negatif ise Sign Flag "1" olarak
işaretlenir.
Syntax
cmp <reg>,<reg>
cmp <reg>,<mem>
cmp <mem>,<reg>
cmp <reg>,<imm>
180. X86 MİMARİSİNE GİRİŞ
X86 ASSEMBLY GİRİŞ
Instruction'lar [Akış Kontrol Instruction'ları]
call, ret (Subroutine call and return)
CALL instruction'ı operand ile verilen adrese uygulama akışını yönlendirirken bir yandan da
kendisinden bir sonra gelen instruction'ın adresini STACK'e yazar, JMP instruction'ından temel farkı
da budur.
RET instruction'ı ise önce STACK'in o anda en üstünde bulunan adres değerini restore eder, yani
uygulama akışını daha önce CALL instruction'ı ile saklanmış olan adrese yönlendirir. Dolayısıyla RET
instruction'ından önce function epilog bölümü gerçekleştirilerek çalıştırılan fonksiyon için STACK'te
ayrılmış olan tüm alanın SUB ESP, 0x??? instruction'ı veya benzeri bir işlem ile geri verilmiş olması
gerekir.
Syntax
call <label>
ret
181. X86 MİMARİSİNE GİRİŞ
STACK ALANININ KULLANIMI
Stack Alanının Çalışma Mekanizması
• Stack yüksek bir adresten düşük adreslere doğru büyür.
• Stack'e veri yazma (PUSH) ve bu veriyi bir register'a okuyarak silme (POP)
işlemi LIFO (Last In First Out) yöntemiyle stack'e veri yazılması ve silinmesine
yol açar. Bu stack'teki herhangi bir alan okunarak (MOV instruction'ı ile) bir
register'a atanamaz ve tam tersi şekilde bir register veya immediate değeri
stack'e yazılamaz anlamına gelmez, LIFO yöntemi ile sadece stack'in alan
tüketimi ve iadesine ilişkin yöntemi ifade ediyoruz.
• Stack sadece PUSH instruction'ı ile büyümez ve POP instruction'ı ile
küçülmez. [SUB ESP, 0x20] benzeri instruction'larla ESP register değeri
küçültülerek büyüyebilir ve [ADD ESP, 0x20] benzeri instruction'larla ESP
değeri artırılarak küçülebilir.
• Stack ASLR uygulanırsa her yüklemede farklı bir adresten başlatılır.
182. X86 MİMARİSİNE GİRİŞ
STACK ALANININ KULLANIMI
Stack'te Saklanan Veriler
• Fonksiyon parametreleri: fastcall gibi calling convention'larda
register'lar da fonksiyonlara parametre vermek için kullanılabilmekle
birlikte diğer convention'larda ve bu iş için ayrılmış register'ların
sayısının yetmediği durumlarda parametreler "CALL" instruction'ı ile
fonksiyon çağrılmadan önce stack'e yazılır. Parametreler genellikle
sağdan sola doğru stack'e PUSH edilir ("printf" gibi değişken sayıda
parametre kabul eden fonksiyonlar için format string parametresinde
olduğu gibi diğer parametreleri tanımlayan bir string başta olduğundan
bu yaklaşım oldukça mantıklıdır).
183. X86 MİMARİSİNE GİRİŞ
STACK ALANININ KULLANIMI
Stack'te Saklanan Veriler [devamı]
• Fonksiyon dönüş adresi: "CALL" instruction'ı çalıştığı anda bu
instruction'dan bir sonraki instruction adresi fonksiyondan "RET"
instruction'ı ile dönüldüğünde EIP register'ına atanmak üzere stack'te
saklanır. Bellek taşma açıklıklarında uygulama akışını manipüle etmek
için hedef alınan başlıca pointer değeri bu değerdir (diğerlerine SEH
pointer değeri, lokal değişken olarak saklanan object veri yapılarının ilk
alanında bulunan Virtual Table pointer değeri örnek verilebilir).
184. X86 MİMARİSİNE GİRİŞ
STACK ALANININ KULLANIMI
Stack'te Saklanan Veriler [devamı]
• Bir önceki fonksiyon frame pointer'ı: Fonksiyon "prolog"larında daha
önce de gördüğümüz gibi ilk yapılan işlem mevcut frame pointer
değeri (EBP) yeni fonksiyonun frame pointer değeri ile ezileceği için
"PUSH EBP" instruction'ı ile stack'e yazılır. Bu değer fonksiyon
"epilog"unda "POP EBP" instruction'ı ile EBP pointer'ını eski frame
pointer'ı haline getirmek için kullanılır.
• Fonksiyon lokal değişkenleri: Fonksiyon "prolog"undan sonra
genellikle fonksiyon lokal değişkenleri için ESP değeri düşürülerek yer
ayrılır. Daha sonra lokal değişkenler için ayrılan bu alana lokal
değişkenler yazılmaya ve bu alandan gerektiğinde okunmaya başlanır.
185. X86 MİMARİSİNE GİRİŞ
CALLING CONVENTIONS
CALLING CONVENTION'LARI NEDEN ANLAMALIYIZ?
Calling convention'lar çağıranın mı çağrılanın mı parametreler için
kullanılan alanı geri vereceği ve hangi register'ların korunmasının
kimin sorumluluğunda olduğu ile ilgilidir.
İyi Niyetli Cevap
Bu sorunun iyi niyetli cevabı farklı compiler'ların ürettikleri kodların
bir arada uyum içinde çalışabilmeleri için stack yönetimi ve
register'ların korunması için aynı yaklaşımları izleme ihtiyacıdır.
186. X86 MİMARİSİNE GİRİŞ
CALLING CONVENTIONS
CALLING CONVENTION'LARI NEDEN ANLAMALIYIZ? [devamı]
Kötü Niyetli Cevap
Inline assembly ile bir kod yazarken veya virüs benzeri bir kodu
mevcut bir uygulamaya yerleştirirken assembly içinden çağrılan
Windows API'lerinin calling convention'ına uygun olarak hareket
edilmezse Stack yapısının düzeni bozulacak, dolayısıyla da
uygulama stack frame'i ile ilgili bu düzensizlikten dolayı hata alarak
sonlanacaktır.
Windows API'leri için "stdcall" convention'ına uygun bir biçimde
stack'teki parametreleri yönetmezsek kodumuzun içinde çalıştığı
proses hata alacaktır.
187. X86 MİMARİSİNE GİRİŞ
CALLING CONVENTIONS
stdcall
Adının çağrıştırabileceğinin aksine aslında tüm derleyiciler için standart
convention değildir ve Microsoft Win32 API tarafından kullanılır. İlk olarak bu
convention'dan söz etmemizin sebebi de budur.
• Stack'in temizlenmesinden çağırılan (callee) sorumludur. Yani çağıran (caller)
parametreleri stack'e push eder, ancak temizleme işlemini gerçekleştirmez.
• Parametreler stack'e sağdan sola doğru push edilir.
• EAX, ECX ve EDX register'ları fonksiyon içinde kullanılır, dolayısıyla
fonksiyondan geri dönüldüğünde farklılaşmış olma ihtimalleri yüksektir. Yani
bunlar caller saved register'lardır.
• Fonksiyon return değeri EAX register'ına yazılır.
188. X86 MİMARİSİNE GİRİŞ
CALLING CONVENTIONS
cdecl
C declaration'ın kısaltması olan bu convention çoğu C derleyicileri tarafından
kullanılır.
• Stack'in temizlenmesinden çağıran (caller) sorumludur. Bu yaklaşım
stdcall'unkinden farklıdır.
• Parametreler stack'e sağdan sola doğru push edilir.
• EAX, ECX ve EDX register'ları fonksiyon içinde kullanılır, dolayısıyla
fonksiyondan geri dönüldüğünde farklılaşmış olma ihtimalleri yüksektir. Yani
bunlar caller saved register'lardır.
• Fonksiyon return değeri EAX register'ına yazılır.
189. X86 MİMARİSİNE GİRİŞ
CALLING CONVENTIONS
Microsoft fastcall
Parametre aktarımında register kullanımına da bir örnek olması amacıyla fastcall
convention'ından da bahsedelim. Bir fonksiyonun bu convention'a göre
derlenmesi için __fastcall anahtar kelimesinin kullanılması gerekir.
• Soldan sağa ilk iki parametre ECX ve EDX register'larında aktarılır.
• Diğer parametreler (bu defa sağdan sola) stack'e yazılır.
• Stack'in temizlenmesinden stdcall'da olduğu gibi çağırılan (callee)
sorumludur.
190. ANALİZ
STATİK ANALİZ [Disassembly]
• X86 mimarisi ve assembly hakkında daha fazla bilgiye sahip
olduğumuza göre biraz daha detaylı bir statik analiz
gerçekleştirebiliriz.
• İlk dinamik analiz çalışmalarımız sırasında uygulamanın bir HTTP
isteğinde bulunduğunu gözlemlemiştik. Tabi bu gözlemi zararlı
yazılıma herhangi bir internet erişimi vermeden gözlemlediğimiz
için aslında indireceği [paket.bin] dosyasında ne olduğunu
henüz bilmiyoruz.
• Ancak ilginç olan konu uygulamanın Import ettiği API
fonksiyonları içinde herhangi bir network veya HTTP API'sinin
bulunmaması.
194. ANALİZ
STATİK ANALİZ [Disassembly]
Import Address Table'da VirtualAllocEx
fonksiyonuyla ilgili alana geçiyoruz.
Burada VirtualAllocEx fonksiyon isminin
üzerinde tıkladıktan sonra "x" tuşuna
tıklayarak bu adrese referans verilen
yerleri gözlemleyelim.
196. ANALİZ
STATİK ANALİZ [Disassembly]
Bu alana ulaştığımızda biraz aşağıda WriteProcessMemory fonksiyonunun
da çağrıldığını görebiliyoruz.
VirtualAllocEx fonksiyonunun return değerinin "mov ebx, eax" instruction'ı
ile "ebx" register'ına aktarıldığını görüyoruz.
197. ANALİZ
STATİK ANALİZ [Disassembly]
WriteProcessMemory fonksiyonunun çağrıldığı instruction'ın hemen
üzerinde toplam 5 adet push instruction'ı var. Bu instruction'lar büyük
ihtimallle bu fonksiyonun aldığı parametreler olmalı.
198. ANALİZ
STATİK ANALİZ [Disassembly]
MSDN'den WriteProcessMemory API'si ile ilgili bilgilere göz attığımızda
fonksiyonun aldığı 5 parametreyi görebiliriz. Parametrelerden 3.sü diğer process'in
address space'ine yazılacak içeriğin barındırıldığı alana işaret eden bir pointer. Bir
zararlı yazılımı analiz ederken bizi merak ettirebilecek bir konu bu, o yüzden bu
parametre'nin nereden geldiğini biraz inceleyelim.
199. ANALİZ
STATİK ANALİZ [Disassembly]
Disassembly ekranında WriteProcessMemory fonksiyonuna verilen 3.
parametrenin dword_42FCC8 pointer'ı olarak belirtildiğini görüyoruz. Şimdi bu
parametreye tıkladıktan sonra bu asrese referans veren noktaları inceleyelim.
200. ANALİZ
STATİK ANALİZ [Disassembly]
"x" tuşuna bastığımızda bu adrese 2
noktada referans verildiğini görüyoruz.
2. sırada bulunan referans şu anda
gözlemlediğimiz alan, dolayısıyla
diğerini incelememizde fayda var. 1.
linke çift tıklayarak bu alanı inceleyelim.
201. ANALİZ
STATİK ANALİZ [Disassembly]
Bu alana geldiğimizde "eax" register
değerinin bu pointer'a atandığını
görüyoruz. "eax" register'ı da hemen 2
instruction yukarıdaki "call
sub_402A70" instruction'ı ile
belirlenmiş olmalı.
202. ANALİZ
STATİK ANALİZ [Disassembly]
Tekrar hatırlayalım halen diğer process'in address alanına yazılacak verinin
nereden geldiğini inceliyoruz. Görünen o ki sub_402A70 adlı fonksiyon bu veriye
işaret eden bir pointer döndürüyor. Şimdi çift tıklayarak bu fonksiyona göz
atalım.
204. ANALİZ
STATİK ANALİZ [Disassembly]
1
2
3
Fonksiyonda biraz aşağıya indiğimizde deneyimli bir tersine mühendislik
uzmanı tarafından normalde bir derleyici tarafından üretildiğine pek şahit
olunmayacak olan bazı instruction'ları görüyoruz. Bu durum zararlı yazılımı
geliştirmiş olan kişinin uygulamanın bu bölümünü bir inline assembly kodu
ile geliştirmiş olma ihtimalini aklımıza getirmelidir. İlginç instruction'lardan
birkaçını işaretledik. Şimdi sırasıyla bu instruction'lara anlam vermeye
çalışalım.
205. ANALİZ
STATİK ANALİZ [Disassembly] 1
Birinci instruction "pusha", 32 bit'lik bir uygulama için bizim ikinci satırı esas
almamız lazım. Instruction'ın yaptığı işlem tüm genel amaçlı register'ları yukarıda
görüldüğü sırada stack'e yazmak. Bir shellcode programcısı bu işlemi kodu
gömdüğü alanda bu register'ları kullandıktan sonra "popa" instruction'ı ile kazasız
belasız tekrar orijinal hallerine döndürmek amacıyla kullanabilir.
206. ANALİZ
STATİK ANALİZ [Disassembly] 2
İkinci ilginç instruction ise "fs:0x30" adresindeki bir veriye işaret ediyor. "fs"
register'ı Thread Environment Block [TEB] veri alanına işaret etmek için kullanılır.
Bu veri yapısının 0x30 offset'inde ise Process Environment Block yer alır.
Derlenmiş bir kodda böyle bir instruction'a rastlamak olası değildir.
207. ANALİZ
THREAD ENVIRONMENT BLOCK (TEB)
Adım-1: PEB’in adresinin bulunması
0:000> !teb
TEB at 7efdd000
ExceptionList: 0026f814
StackBase: 00270000
StackLimit: 0026e000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 7efdd000
EnvironmentPointer: 00000000
ClientId: 00001920 . 00001928
RpcHandle: 00000000
Tls Storage: 7efdd02c
PEB Address: 7efde000
LastErrorValue: 0
LastStatusValue: 0
Count Owned Locks: 0
HardErrorMode: 0
fs:[0]
fs:[30]
0x30
kernel32.dll'in adresinin bulunması
2
WinDbg binary debugger'ı ile bazı Windows veri yapılarını kolayca inceleyebiliriz.
Yukarıda böyle bir örnek verilmektedir.
208. ANALİZ
STATİK ANALİZ [Disassembly] 3
Yukarıdaki instruction'lar başlangıçta biraz kafa karıştırıcı gelebilir, ancak iyi bir
tersine mühendislik uzmanı bu tür instruction'lara hakim olabilmelidir. fs:30h
alanında bulunan PEB referansı "esi" instruction'ına atıldıktan sonra, PEB'in 0xC
offset'indeki değer tekrar "esi"a atanmakta, bu adresin de 0x1C offset'indeki
değer de tekrar "esi"a atanmaktadır. Son adımda "esi" register'ının neyin adresini
barındıracağını inceleyelim.
209. ANALİZ
PROCESS ENVIRONMENT BLOCK (PEB)
Adım-2: _PEB_LDR_DATA veri yapısının bulunması
0:000> dt nt!_peb 7efde000
ntdll!_PEB
+0x000 InheritedAddressSpace : 0 ''
+0x001 ReadImageFileExecOptions : 0 ''
+0x002 BeingDebugged : 0x1 ''
+0x003 BitField : 0x8 ''
+0x003 ImageUsesLargePages : 0y0
+0x003 IsProtectedProcess : 0y0
+0x003 IsLegacyProcess : 0y0
+0x003 IsImageDynamicallyRelocated : 0y1
+0x003 SkipPatchingUser32Forwarders : 0y0
+0x003 SpareBits : 0y000
+0x004 Mutant : 0xffffffff Void
+0x008 ImageBaseAddress : 0x01380000 Void
+0x00c Ldr : 0x77240200 _PEB_LDR_DATA
+0x010 ProcessParameters : 0x002f2178 _
....
fs:[30]
0x0c
kernel32.dll'in adresinin bulunması
3
0xC offset'inde process'in address alanına yüklenmiş olan DLL'ler ile ilgili yükleme veri yapısına bir
referans bulmaktayız. Shellcode geliştirme deneyimi olan bir kişi size bu adımların içinde "LoadLibrary"
fonksiyonunu barındıran Kernel32.dll kütüphanesinin process'in adres alanındaki tabanını bulmak için
gerekli ilk adımlardan birisi olduğunu söyleyecektir. LoadLibrary fonksiyonu uygulama içinde dinamik
olarak farklı DLL'lerin yüklenebilmesi için kullanılır.
210. ANALİZ kernel32.dll'in adresinin bulunması
_PEB_LDR_DATA
Adım-3: Modül zincir listelerinin bulunması
0:000> dt _PEB_LDR_DATA 0x77240200
ntdll!_PEB_LDR_DATA
+0x000 Length : 0x30
+0x004 Initialized : 0x1 ''
+0x008 SsHandle : (null)
+0x00c InLoadOrderModuleList : _LIST_ENTRY [ 0x2f4cf8 -
0x2f5990 ]
+0x014 InMemoryOrderModuleList : _LIST_ENTRY [ 0x2f4d00
- 0x2f5998 ]
+0x01c InInitializationOrderModuleList : _LIST_ENTRY [
0x2f4d98 - 0x2f59a0 ]
+0x024 EntryInProgress : (null)
+0x028 ShutdownInProgress : 0 ''
+0x02c ShutdownThreadId : (null)
0x1c
3
Bu adresin 0x1C offset'inde ise yüklenme sırasına göre modül (yani DLL) bilgileri
veri yapılarının başlangıç adresi tutulur.
211. ANALİZ kernel32.dll'in adresinin bulunması
MODÜL ZİNCİR LİSTESİ
Adım-4: Başlatılma sırasına göre modül zincir listesinin izlenmesi
0:000> dt _LIST_ENTRY 0x7724021c
ntdll!_LIST_ENTRY
[ 0x2f4d98 - 0x2f59a0 ]
+0x000 Flink : 0x002f4d98 _LIST_ENTRY [ 0x2f5230 - 0x7724021c ]
+0x004 Blink : 0x002f59a0 _LIST_ENTRY [ 0x7724021c - 0x2f5118 ]
0:000> dt _LIST_ENTRY 0x002f4d98
ntdll!_LIST_ENTRY
[ 0x2f5230 - 0x7724021c ]
+0x000 Flink : 0x002f5230 _LIST_ENTRY [ 0x2f5118 - 0x2f4d98 ]
+0x004 Blink : 0x7724021c _LIST_ENTRY [ 0x2f4d98 - 0x2f59a0 ]
0:000> dt _LIST_ENTRY 0x002f5230
ntdll!_LIST_ENTRY
[ 0x2f5118 - 0x2f4d98 ]
+0x000 Flink : 0x002f5118 _LIST_ENTRY [ 0x2f59a0 - 0x2f5230 ]
+0x004 Blink : 0x002f4d98 _LIST_ENTRY [ 0x2f5230 - 0x7724021c
]
1
2
3
Bu veri yapısı bir zincir listedir
[linked list] ve zincirin her bir
bileşeni bir sonra gelene işaret
eden adresi ilk 4 byte'ında
barındırır.
212. ANALİZ kernel32.dll'in adresinin bulunması
MODÜL ADI
Adım-5: Modül adının bulunması
0:000> dd 0x002f4d98 + 20
002f4db8 77185bc4 00004004 0000ffff 002f59cc
002f4dc8 772448e0 521ea8e7 00000000 00000000
0:000> db 77185bc4
77185bc4 6e 00 74 00 64 00 6c 00-6c 00 2e 00 64 00 6c 00 n.t.d.l.l...d.l.
77185bd4 6c 00 00 00 14 00 16 00-e0 5b 18 77 5c 00 53 00 l........[.w.S.
77185be4 59 00 53 00 54 00 45 00-4d 00 33 00 32 00 5c 00 Y.S.T.E.M.3.2..
77185bf4 00 00 90 90 90 90 90 8b-ff 55 8b ec 51 51 83 65 .........U..QQ.e
77185c04 fc 00 53 56 8b 35 0c 02-24 77 57 81 fe 0c 02 24 ..SV.5..$wW....$
77185c14 77 74 31 8d 45 f8 50 6a-09 8b fe 8b 36 6a 01 ff wt1.E.Pj....6j..
Ayrıca bu adresten [decimal] 20 byte sonraki 4 byte'lık alanda da bu veri yapısının ait olduğu DLL'in
adının tutulduğu alanın pointer'ını barındıran veri yer alır. Bu alanlar sırasıyla incelenerek
Kernel32.dll'in kaydına ulaşıp ulaşmadığımız tespit edilebilir. Zincir halkalarında her bir modül'ün adresi
de bulunur. Sonraki adımlar bu modül başlangıç adresinden başlayarak modülün (yani DLL'in) Export
tablosu içinde gezinmeye başlamak ve LoadLibrary fonksiyon adresini bulmaya çalışarak devam
edecektir. Biz örneği daha da karmaşıklaştırmamak için bu adımı burada sonlandırıyoruz. Bu aşamada
zararlı yazılım yazarının bazı DLL'leri yüklediğinin kolay görünür olmaması için çaba gösterdiğini
söyleyebiliriz.
213. ANALİZ
STATİK ANALİZ [Disassembly]
Bu fonksiyonda daha aşağılara ilerlediğimizde
bazı HEX değerlerin stack'e push edildiğini
görüyoruz. Bunları "R" karakteri ile ASCII
karakterlere dönüştürebiliriz.
Zararlı yazılım yazarı
strings incelemesinden
[wininet.dll] DLL adının
stack'e yazıldığı ortaya
çıkmasın diye işlevsiz
opcode'ları da assembly
koduna eklemiş.
217. ANALİZ
STATİK ANALİZ [Disassembly]
İncelediğimiz kodun içinde pek çok
defa çağrıldığını gördüğümüz bir
fonksiyon da dikkatimizi çekebilir.
Yeterli zaman olması halinde bu
fonksiyona verilen iki parametrenin
belli bir DLL'in başlangıç adresi ve bir
fonksiyon adının basit bir algoritma ile
hash'lenmiş değeri olduğunu tespit
edebilirdik. Bu sayede çağrılan
fonksiyonların neler olduğunu statik
analiz ile tespit etmemiz çok
zorlaştırılmıştır. Ancak dinamik
analizde hangi fonksiyonların çağrıldığı
açıkça görülebilir.
218. ANALİZ
STATİK ANALİZ [Disassembly]
Fonksiyon kodları içinde biraz daha
aşağıya indiğimizde stack'e yazılan
bir dizi HEX veri daha görebiliriz.
Ancak bu verileri "R" komutuyla
ASCII karşılıklarına
dönüştüremiyoruz, çünkü dikkat
ederseniz arada NULL [00]
karakterleri bulunması bu
karakterlerin Unicode karakterler
olabileceğine işaret etmektedir.
219. ANALİZ
STATİK ANALİZ [Disassembly]
Bu karakterleri kodun içinden daha rahat çekebilmek için IDA'nın 8 opcode byte'ına kadar
görüntüleme ayarını Options penceresinden yapalım.
221. ANALİZ
STATİK ANALİZ [Disassembly]
Kopyaladığımız veriyi Notepad++
ekranına yapıştırdıktan sonra
sadece push edilen 2. ve 5. byte'lar
arasını ALT tuşuna basarken mouse
ile tarayarak kopyalayalım.
223. ANALİZ
STATİK ANALİZ [Disassembly]
Kalan son veriyi HxD veya farklı bir
Hexadecimal bir editöre
kopyaladığımızda sağ tarafta
Unicode karakter karşılıklarını
görebiliriz [Microsoft Internet
Explore]
226. ANALİZ
STATİK ANALİZ [Disassembly]
Bu alanda da aşağıdaki verinin stack'e
yazıldığını ortaya çıkarmış olduk:
[http://www.btr-mlwsunucu.com/paket.bin]
227. ANALİZ
DİNAMİK ANALİZ [Debugging]
Bu noktaya kadar analizin tamamını statik
analiz olarak yapmanın kulağa geldiği kadar
hoş olmadığını farketmiş olmalıyız.
Zararlı yazılımın farklı bir proses'in address
alanına yazdığı verinin ne olduğunu anlamanın
en pratik yolu dinamik analizden faydalanmak
olacaktır.
Bunun için zararlı yazılımımızın çalışacağı
Windows bilgisayarla aynı karantina
subnet'inde bulunmakta olan Kali
bilgisayarımız üzerindeki Honeypot servislerini
[inetsim] başlatalım.
228. ANALİZ
DİNAMİK ANALİZ [Debugging]
Decompress edilmiş olan uygulamamızın ASLR
desteğini daha önceden kaldırmıştık (çünkü
başka türlü çalışmıyordu). Bu imkan sayesinde
statik analizdeki VA adreslerimizin debug
ederken de aynı kalacağından emin olabiliriz.
Şimdi tam farklı process'in address alanına
yazılacak olan verinin Stack'e yazıldığı
instruction'ın adresi olan [0x401E79] adresine
bir breakpoint koyalım.
230. ANALİZ
DİNAMİK ANALİZ [Debugging]
F7 tuşu veya Step Into düğmesine basarak Stack'e farklı process'in
address alanına yazılacak olan verinin adresini yazalım.
Bu işlem gerçekleştikten hemen sonra da yazılan adresin işaret ettiği
alanı daha iyi gözlemleyebilmek için Stack'te bu adrese sağ tıklayarak
[Follow in Dump] seçeneğini seçelim.
231. ANALİZ
DİNAMİK ANALİZ [Debugging]
Sol altta bulunan Memory Dump alanındaki
verileri daha iyi görebilmek için bu alana sağ
klikleyerek [Hex/ASCII (16 bytes)] seçeneğini
seçelim.
232. ANALİZ
DİNAMİK ANALİZ [Debugging]
Anlaşılan o ki söz konusu veri [www.btr-
mlwsunucu.com/paket.bin] HTTP isteğine
gelen yanıttan oluşuyor. Inetsim servisi
sayesinde bu yanıt gerçek paket.bin dosyası
değil de sahte bir HTML yanıtından oluşuyor.
235. ANALİZ
PAYLOAD ANALİZİ
IDA Pro'da bu dosyayı incelediğimizde dosya
normal bir PE dosyası olmadığından paket.bin
dosyasının içeriğini veri olarak algılanacaktır.
237. ANALİZ
PAYLOAD ANALİZİ
Kod bölümü hemen normal bir uygulamadaki
gibi grafik halinde görüntülenmeyebilir,
uygulama grafiğini görebilmek için [Space]
tuşuna basınız.
238. ANALİZ
PAYLOAD ANALİZİ Grafik gösterimde sağdaki koyu mavi çizgi bir
döngü olduğuna işaret ediyor. Bu shellcode'un
encoded bir shellcode olması durumunda
dinamik analiz daha doğru bir tercih olacaktır.
Zararlı uygulamanın internete erişmesi yerine
paket.bin dosyasını inetsim tarafından
sağlayarak dinamik analiz gerçekleştirilebilir.
239. ANALİZ
PAYLOAD ANALİZİ
• Gerçekte bu payload'un tam olarak ne yaptığını anlamak için
ciddi bir süreyi tersine mühendislik için harcamamaız gerekebilir.
• Bu payload bir meterpreter reverse_tcp payload'u ve karşı
tarafta bir handler tarafından karşılanması gerekir.
• Gerçek payload'un ne yaptığının dinamik analizle görülmesi için
kurum ağından bağımsız bir ADSL hattı ve yine bir sanal makine
kullanılarak inceleme yapılabilir, elbette çok dikkatli olmak
kaydıyla.
240. ANTI-DISASSEMBLY
ANTI-DISASSEMBLY TEKNİKLERİ
• Uygulamamızda bir anti-disassembly örneği barındırmadık,
çünkü yeterli zaman ayrılmaması durumunda ve henüz konuya
yeni kişiler için bu durumların tespiti ve çözülmesi zor olabilir.
• Ancak deneyimli tersine mühendislik uzmanları için bu
tekniklerin tespiti ve ortadan kaldırılmasının
zannedilebileceğinden daha kolay olacağını söyleyebiliriz.
241. ANTI-DISASSEMBLY
ANTI-DISASSEMBLY TEKNİKLERİ
• Disassembler'ları yanlış yönlendirmek ve disassembly kodunun
uygulamanın gerçek davranışından farklılaştırmak için pek çok
teknik kullanılabilir, örneğin:
• [EB FF C0 48] opcode'ları "EB FF - jmp 1", geçerli olmayan bir
"C0" opcode'u ve "48 - dec eax" olarak disassemble
edilecektir.
• Gerçekte ise "jmp 1" instruction'ı Instruction Pointer'ı "EB
FF" opcode'larının başından itibaren 1 byte ileriye atlatacak
ve "FF C0 - inc eax" olarak, "48 - dec eax" olarak
çalıştırılacaktır.
• Çözüm bu işe yaramayan ve karmaşıklığa neden olan opcode'ları
NOP ile yamalayarak ortadan kaldırmaktır.