Куки в Windows

Здравей читателю. С тази статия започвам поредица от статии за механизма на прихващането на съобщения в прозореца и на жаргона на програмистите, механизма на куките в операционните системи Windows. Темата за куките е популярна в много форуми за програмиране. Материалът на тези статии е предназначен за начинаещ потребител, примерите ще бъдат в Delphi. Тази статия ще очертае основните принципи на механизма на куката и ще напише пример за keylogger.

И така, нека да започнем, какво представлява механизмът за закачане в Windows? В операционната система Windows куката е механизъм, чрез който специална функция прихваща събития (като съобщения, въвеждане с мишката или клавиатурата), преди те да достигнат до приложението. След това тази функция може да реагира на събития и в някои случаи да ги промени или отмени. Функциите, които получават известия за събития, се наричат ​​филтърни функции и се различават по типовете събития, които улавят. Пример е филтърна функция за улавяне на всички събития на мишката или клавиатурата. Ако множество филтърни функции са прикрепени към една и съща кука, Windows внедрява опашка от функции, като последната прикачена функция е в началото на опашката и първата функция в края на опашката.

Когато една или повече филтърни функции са прикачени към кука и настъпи събитие, което задейства куката, Windows извиква само първата функция в опашката на филтриращи функции. Извикването на всеки следващ манипулатор зависи изцяло от предишния манипулатор. Ако някой манипулатор не извика следващия, тогава целевият прозорец няма да получи желаното съобщение и следователно неговата функция на прозореца няма да бъде извикана.

Куките предоставят мощни функции за Windows приложения. Приложенията могат да използват кукички за следните цели: Обработка или промяна на всички съобщения, предназначени завсички диалогови прозорци, кутии за съобщения, ленти за превъртане или менюта на едно приложение (WH_MSGFILTER) Запис или възпроизвеждане на събития от клавиатура и мишка (WH_JOURNALRECORD, WH_JOURNALPLAYBACK). Обработване, модифициране или изтриване на събития от клавиатура (WH_KEYBOARD). Обработване, модифициране или отмяна на събития от мишката (WH_MOUSE). Отговор на определени системни действия , което позволява разработването на компютърно базирани приложения за обучение (WH_C) BT). Предотвратяване на извикването на друга филтърна функция (WH_DEBUG).

Куките се обработват чрез функциите SetWindowsHookEx, UnhookWindowsHookEx, следващият манипулатор се извиква чрез функцията CallNextHookEx. Преди версия 3.1 Windows предоставяше функциите SetWindowsHook, UnhookWindowsHook и DefHookProc за управление на кукички. Тези функции все още се изпълняват в Win32 само за съвместимост с по-стари приложения и не се препоръчват за по-нови проекти.

Да започнем отначало, функцията SetWindowsHookEx HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId );

Да отидем по-нататък. Всички филтърни функции трябва да бъдат декларирани, както следва: LRESULT CALLBACK FilterFunc(int nCode, WPARAM wParam, LPARAM lParam)

Да се ​​обадя на следващияфункцията в опашката с кукички е функцията CallNextHookEx LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam );

И така, получихме основната информация за куките. Сега трябва да започнем да тренираме. Най-честият проблем, повдигнат във форумите за програмисти, свързан с куките, е проблемът с писането на кийлогъри. Това е keylogger, който сега ще напишем.

За да създадем кука на клавиатурата, трябва да посочим кода WH_KEYBOARD, когато извикваме функцията SetWindowsHookEx. Windows извиква HKA, когато функциите Getmessage или Peekmessage ще върнат съобщения WM_KEYUP, WM_KEYDown. Параметърът CODE може да има следните стойности HC_ACTION Windows предизвиква този код, когато съобщението се премахва. HC_NORMOVE WINDE OWS предизвиква процесор с този код, когато съобщението от клавиатурата не е премахнато от реда, тъй като приложението е причинило функцията PEEKMESSAGE с Параметър PM_NOREMOVE. Извикването на куката с този код не е гарантирано, че ще премине действителното състояние на клавиатурата. Програмистът трябва да е наясно с възможността за такава ситуация.

Параметърът wParam в манипулатора на кука WH_KEYBOARD съдържа кода на виртуалния ключ (например VK_F1, VK_RETURN, VK_LEFT). Параметърът lParam се декодира по следния начин. Битове 0-15 съдържат броя повторения на натиснатия клавиш в случай на залепване. Битове 16-23 съдържат кода за сканиране на натиснатия клавиш. Това е код, зависим от хардуера, който зависи от конкретната клавиатура. Бит 24 е зададен на единица, ако натиснатият клавиш е разширен клавиш (функционална или цифрова клавиатура), в противен случай 0. Битове 25-28 са запазени. Бит 29 е зададен, ако е натиснат бутонът Alt. Бит 30 ни казва състоянието на клавиша преди изпращането на съобщението. малкое равно на единица, ако бутонът е бил натиснат преди изпращане на съобщението, ако бутонът не е бил натиснат преди това, тогава битът е нула. Бит 31 показва текущото състояние на ключа. То е нула, ако бутонът е натиснат, и единица, ако не е натиснат.

Как ще запазим въведения текст? Разбира се, най-лесният начин е да запишете прихванатия код на натиснатия клавиш във файл, това е записване във файл веднага във функцията филтър. Но файловите I/O операции не са много бързи операции и това води до общо намаляване на производителността на системата при въвеждане на какъвто и да е текст (обаче, на по-новите машини това едва ли се забелязва). Затова ще създадем специален прозорец на сървъра, на който ще изпратим кодовете на натиснатите клавиши, този прозорец ще получи кодовете на натиснатите клавиши и ще ги запази в буфера си и ще изхвърли буфера във файл, когато достигне определен размер.

Създаването и премахването на кука, мисля, не е особен проблем, така че веднага ще продължа към самия манипулатор на кука. function KeyHook(CODE, WParam, LParam: DWORD): DWORD; stdcall; var ServerWnd: THandle; ScanCode:integer; започнете ако CODE = HC_ACTION тогава ако ((LParam или (1 shl 30))=LParam) тогава започнете ServerWnd:=FindWindow(nil,'Прост кийлогър'); GetKeyboardState(KeybrdState); ScanCode:=(LParam shr 16)и $FF; if ToAscii(WParam,ScanCode,KeybrdState,@Symbol,0)>0 then PostMessage(ServerWnd, WM_KEYEVENT, ord(Symbol[0]), LParam) else PostMessage(ServerWnd, WM_KEYEVENT, 0, LParam); край; Резултат:=CallNextHookEx(HookHandle, код, WParam, LParam); край;

Основният проблем при писането на куки за клавиатура е, че само кодът за сканиране на натиснатия клавиш и неговият виртуален код се предават на манипулатора на куките. Виртуален код и код за сканиране казватни кой клавиш е натиснат, но не ни казват точно какво е написано. Обяснявам, дори и да въведем български текст, тогава кодовете на английските клавиши ще се предават на куката на клавиатурата, т.е. въвеждаме думата "hello" и манипулаторът на куките ще бъде предадено "GHBDTN". Или, например, натискаме Shift върху числото 7 и се въвежда знак &, но само кодът на числото 7 ще бъде предаден на куката на клавиатурата.За да конвертирате кода за сканиране и виртуалния код във въведения текстов знак, трябва да използвате функцията ToAscii (или ToUnicode).lpKeyState, LPWORD lpChar, UINT uFlags );

Първият параметър е виртуален код, вторият е кодът за сканиране, третият параметър е указател към масив, в който се съхранява състоянието на клавиатурата, четвъртият е указател към променлива, в която ще бъде записан символът, петият параметър е флаг, който определя дали менюто е активно. Този параметър трябва да бъде 1, ако менюто е активно, или 0 в противен случай Функцията връща броя символи, получени в резултат на преобразуването. Състоянието на клавиатурата може да се получи с помощта на функцията GetKeyboardState.

Обратно в нашата филтърна функция. ServerWnd:=FindWindow(nil,'Simple keylogger '); GetKeyboardState(KeybrdState); ScanCode:=(LParam shr 16)и $FF; ако ToAscii(WParam,ScanCode,KeybrdState,@Symbol,0)>0 след това Публикуване на съобщение(ServerWnd, WM_KEYEVENT, ord(Symbol[0]), LParam) else PostMessage(ServerWnd, WM_KEYEVENT, 0, LParam);

Първо получаваме състоянието на клавиатурата, след това получаваме кода за сканиране от параметрите на LParam и извикваме функцията ToAscii. Ако резултатът му не е равен на нула, т.е. ако резултатът му не е празен, тогава изпращаме съобщение до прозореца на сървъра със заглавие „Прост кейлогър“ (необходими са числа в заглавиетосамо заради неговата уникалност). Сами декларирахме съобщението WM_KEYEVENT WM_KEYEVENT=WM_USER+1

А ето и самия манипулатор на съобщения WM_KEYEVENT procedure TMainForm.KeyMessageHandler(var Msg: TMessage); var KeyName:array[0..99] of char; _MSG:TMsg; begin GetKeyNameText(Msg.LParam, KeyName, sizeof(KeyName)); BufferWrite('13) след това започнете BufferWrite(','); Име на ключ[0]:=chr(Msg.WParamLo); Име на ключ[1]:=#0; BufferWrite(KeyName); край; BufferWrite('>'); inc (брояч); ако Counter>MaxSimbolGroup тогава започнете BufferWrite(NewLine); Време на запис; BufferWrite(NewLine); Брояч:=0; край; край;

Използвахме функцията GetKeyNameText, за да получим текстов препис на натиснатия клавиш, като използвахме неговия код за сканиране. Пълният текст на DLL и приложението е в архива, прикачен към тази статия.

Ако погледнем получения дневник, ще видим следния текст в .