Rootkit Geliştirme Temelleri-II

Windows işletim sisteminde hem kullanıcı seviyesinde(user level) hem de çekirdek
seviyesinde(kernel
level) hook işlemi yapılabilir. Hook işlemi sistemin veya diğer
uygulamaların kullandığı belli fonksiyonların işlevini değiştirmek veya
incelemek amacıyla yapılır. İşleyişi değiştirmek için fonksiyonu çalışma
anında yamamak veya ürettiği sonucu isteği yapan uygulamaya göndermeden
önce modifiye etmek gibi seçenekler uygulanabilir. 

Bu yazıda SSDT
(system service dispatch table) kullanarak çekirdek seviyesinde hook
işlemi ile dosya ve süreçlerin gizlenmesi ele alınmıştır. Bahsedilen
uygulamaların kaynak kodlarının tam halini şu adresten indirebilirsiniz.

Kodlar Windows 7 professional x86 işletim sisteminde test edilmiştir.

SSDT Kullanarak Çekirdek Seviyesinde Hook işlemi

Windows
işletim sisteminde sistem servislerinin adresleri SSDT’de tutulur. SSDT
bir servis fonksiyonunun hafızadaki adresine erişmek için kullanılır.
KeServiceDescriptorTable adındaki başka bir tablo da, SSDT üzerinde
Ntoskrnl.exe tarafından sağlanan temel sistem fonksiyonlarının
adreslerinin bulunduğu bölümü gösteren bir girdi içerir. SSDT’de, işlevi
değiştirilmek istenen fonksiyonların yerine belirlenen fonksiyonların
adresleri atanıp, fonksiyonlar çağırıldığında kontrolü rootkit yazarında
olan yeni fonksiyonlar aracılığıyla istenen veri döndürülebilir. Örnek
olarak süreç gizlemek için ZwQuerySystemInformation ve dizin gizlemek
için ZwQueryDirectoryFile fonksiyonlarına hook işlemi ele alınmıştır.

1. SSDT’nin Yazma Korumasının Kaldırılması

SSDT
gibi önemli sistem yapılarınının gelişigüzel değiştirilmesini
engellemek için hafızanın bu bölümlerinde yazma koruması
uygulanmaktadır. Bu korumayı aşmak için Win7 x86 ve öncesi sistemlerde
şu kod kullanılabilir. (modwp.c)
Kodun
mantığı temel olarak intel mimarisinde yazma koruması için kullanılan
control register’ın CR0 bitinin değerini değiştirmeye dayanmaktadır.
disableWP_MDL fonksiyonu şu şekilde kullanılır.
WP_GLOBALS wpGlobals;
wpGlobals = disableWP_MDL(    KeServiceDescriptorTable.KiServiceTable,
KeServiceDescriptorTable.nSystemCalls);
  
if((wpGlobals.pMDL == NULL) || (wpGlobals.callTable == NULL)) {
       return(STATUS_UNSUCCESSFUL) ;
}
pMDL = wpGlobals.pMDL;
systemCallTable = (PVOID*)wpGlobals.callTable;
//hookSSDT(oldfunction, newfunction, systemCallTable)

2. HookSSDT Fonksiyonu

(hookssdt.c, ssdt.h)

hookssdt.c
dosyası getSSDTIndex, NtRoutineAddress, hookSSDT ve unHookSSDT olmak
üzere dört fonksiyon içermektedir. hookSSDT ve unHookSSDT ana
fonksiyonlar, diğerleri ise bu fonksiyonların kullandığı yardımcı
fonksiyonlardır. ssdt.h dosyası da gerekli veri yapısı tanımlarını
içerir.

BYTE* hookSSDT(BYTE* apiCall, BYTE* oldAddr, DWORD* callTable) {

   PLONG target ;

   DWORD indexValue;

   indexValue = getSSDTIndex(apiCall);

   target = (PLONG) &(callTable[indexValue]);

   return( (BYTE*)InterlockedExchange(target, (LONG)oldAddr));

}

Fonksiyon
parametre olarak sırasıyla değiştirilecek fonksiyonun adresi, yeni
fonksiyonun adresi ve SSDT’nin adresini alır. getSSDTındex fonksiyonunu
kullanarak fonksiyonun SSDT tablosundaki yerini bulur ve atomik(işleyiş
sırasında kesilemez) olarak çalışan InterlockedExchange fonksiyonu ile
yeni adresi ilgili indexe yazar.

unHookSSDT
fonksiyonu da hook kaldırılmak istendiğinde çağırılır. hookSSDT
fonksiyonunun ilk iki parametresini yer değiştirerek çağırmak da aynı
işi görür ama karışıklık olmaması açısından ayrı bir fonksiyon
yazılmıştır.

getSSDTIndex fonksiyonu adından da anlaşılacağı gibi parametre olarak verilen fonksiyonun SSDT indexini döndürür.

NtRoutineAddress ise kiServiceTable’da, verilen SSDT indexe karşılık gelen adresi döndürür.

3. Yeni ZwQueryDirectoryFile ve ZwQuerySystemInformation Fonksiyonlarının Yazılması

(ZwQueryDirectoryFile.c, ZwQuerySystemInformation.c)
İlk bakışta uzun C kodları gibi görünseler de temel mantıkları basittir.
ZwQueryDirectoryFile.c:
Fonksiyon
çağırıldığında ilk olarak kendi parametreleri ile orijinal fonksiyonu
çağırır. Geriye parametre olarak gelen file handle’ın gösterdiği
dizindeki dosyaların bilgilerinin bulunduğu bir bağlı listenin
başlangıcı döndürür. Devamında bu liste taranıp, RtlCompareMemory
fonksiyonu kullanılarak hiddenDirName adlı değişkende belirlediğimiz
öneki taşıyan dosyalar kontrol edilir. Koşula uyan dosyalar listeden
çıkarılır. Bu sayede fonksiyon tamamlandığında, ZwQueryDirectoryFile
fonksiyonunu çağıran uygulama dosyaların listesini taradığında
çıkarılmış olan dosyalardan haberi olmaz.
ZwQuerySystemInformation.c:
Bu
fonksiyonun da benzer mantıkla çalışır. Yine başlangıçta orijinal
fonksiyonu çağırıp süreçlerin listesini elde eder. Daha sonra hiddenProc
değişkeninde belirlediğimiz isimdeki süreci arar(yine önek ya da birden
fazla isim de kullanılabilir). Eğer süreç bulunursa listeden çıkartılır
ve tüm liste tarandıktan sonra fonksiyon sonlanır. Süreç listesini
ZwQuerySystemInformation fonksiyonu aracılığıyla elde eden uygulamalarda
(windows görev yöneticisi gibi) listeden çıkarılmış süreç gözükmez.

4. KMD’nin Ana Dosyasının Güncellenmesi

(driver.c)
Son
olarak driver.c dosyasının, ring3 uygulamadan komut geldiğinde hookları
aktif edecek ve driver kaldırılırken hookları kaldıracak şekilde
güncellenmesi gerekmektedir. Bunun için adım 1’deki disableWP_MDL
fonksiyonunu kullanarak yazma korumasını kaldıran ve adım 2’deki
hookSSDT fonksiyonunu kullanarak hookları aktif eden hook isminde bir
fonksiyon yazılmıştır. Bu fonksiyon “Rootkit Geliştirmenin Temelleri 1”
adlı ilk yazıda hazırlanan ring3 seviyesindeki uygulamadan komut alma
bölümüne eklenmiştir. Yine aynı yazıda hazırlanan sendcmd uygulamasında
bir değişiklik yapmaya gerek yoktur. driver.c dosyasında yapılan bir
diğer değişiklik ise Unload fonksiyonu içinde unhook fonksiyonu
çağırılmıştır. Bu sayede driver kaldırıldığında sistem sorunsuzca
çalışmaya devam edebilir.

5. Kodların Test Edilmesi

Kodlar ilk yazıda anlatılan araçlar ve yöntemler kullanılarak derlenip driver yüklenir. Driver derlendiğinde örnek çıktı:

Örnek
kodlarda gizlenecek uygulama “notepad”, gizlenecek dosya/dizin öneki
ise “000gizli” olarak belirlenmiştir. Driver çalışmasına rağmen henüz
kullanıcı seviyesi uygulamadan komut gönderilmediği için hooklar aktif
olmamıştır. hooklar aktif olmadan önceki ekran görüntüsü şu şekildedir.

İlk
yazıda hazırlanan sendcmd uygulaması çalıştırılıp, görev yöneticisinden
notepad’in masaüstünden de 000gizliaa dosyasının kaybolduğu gözlenir.

Driver çalışırken oluşan DbgView çıktısı:

Kodların
tamamını satır satır incelemek şart olmasa da açıklanan bölümleri kod
üzerinde çalışmak faydalı olabilir. WinAPI fonksiyonlarıyla ilgili
detaylı bilgiye http://msdn.microsoft.com/library/default.aspx adresinden ulaşılabilir.
Kaynaklar:
The Rootkit Arsenal 1st Edition by R. Bill Blunden
Subverting The Windows Kernel by Greg Hoglund, James Butler
Professional Rootkits by Ric Viele

Onur ALANBEL <onur.alanbel@bga.com.tr>