Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 2 сентября!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

ПРОГРАММИРОВАНИЕ НА C и С++



Программисты долго мучаются с кодом прогаммы, изучают С++, WinAPI функции, MSDN. Потом пишут банальную систему защиты или навешивают банальный протектор, а крэкеры и реверсеры справляются с такой защитой за 5 минут. В итоге, продажи программы почти нулевые. Чтобы такого не допустить, тут самому надо немного поднабрать опыта отладки, реверсинга, тот же отладчик Ollydbg изучить или дизассемблер IDA Pro. Но где искать по крохам эти знания? Нет, конечно можно годами "методом тыка" разбираться, но куда быстрее видеокурс специальный посмотреть. Вот тут он есть: ссылка. Автор курса с большим опытом и объясняет понятно, я из этого курса много узнал про то как работает компьютер, процессор, про инструменты специальные и как с ними работать. Мои коллеги программисты на работе ничего такого и не знают, теперь я им нос утру.

Создание системных ловушек Windows на Borland C++ Builder 5

А.Е. Шевелёв

Прежде чем излагать материал я хочу заметить, что цель данной работы - показать как пишутся ловушки Windows вообще. Подробности, по мере возможности, я буду опускать (их можно найти в поставляемой со средой разработке справке).

Для начала определим, что именно мы хотим сделать.

Цель: написать программу, которая будет вызывать хранитель экрана при перемещении курсора мыши в правый верхний угол и выдавать звуковой сигнал через встроенный динамик при переключении языка с клавиатуры.

Предполагается, что такая программа должна иметь небольшой размер. Поэтому будем писать её с использованием только WIN API.

Понятие ловушки.

Ловушка (hook) - это механизм, который позволяет производить мониторинг сообщений системы и обрабатывать их до того как они достигнут целевой оконной процедуры.

Для обработки сообщений пишется специальная функция (Hook Procedure). Для начала срабатывания ловушки эту функцию следует специальным образом "подключить" к системе.

Если надо отслеживать сообщения всех потоков, а не только текущего, то ловушка должна быть глобальной. В этом случае функция ловушки должна находиться в DLL.

Таким образом, задача разбивается на две части:

  1. Написание DLL c функциями ловушки (их будет две: одна для клавиатуры, другая для мыши).
  2. Написание приложения, которое установит ловушку.

Написание DLL.

Создание пустой библиотеки.

С++ Builder имеет встроенный мастер по созданию DLL. Используем его, чтобы создать пустую библиотеку. Для этого надо выбрать пункт меню File->New: В появившемся окне надо выбрать "DLL Wizard" и нажать кнопку "Ok". В новом диалоге в разделе "Source Type" следует оставить значение по умолчанию - "C++". Во втором разделе надо снять все флажки. После нажатия кнопки "Ок" пустая библиотека будет создана.

Глобальные переменные и функция входа (DllEntryPoint).

Надо определить некоторые глобальные переменные, которые понадобятся в дальнейшем.

 #define UP 1// Состояния клавиш
 #define DOWN 2
 #define RESET 3
 
 int iAltKey;                  // Здесь хранится состояние клавиш
 int iCtrlKey;
 int iShiftKey;
 
 int KEYBLAY;// Тип переключения языка
 bool bSCRSAVEACTIVE;// Установлен ли ScreenSaver
 MOUSEHOOKSTRUCT* psMouseHook;                    // Для анализа сообшений от мыши
 

В функции DllEntryPoint надо написать код, подобный нижеприведённому:

 if(reason==DLL_PROCESS_ATTACH)// Проецируем на адр. простр.
 {
 HKEY pOpenKey;
  char* cResult="";    // Узнаём как перекл. раскладка
 long lSize=2;
 KEYBLAY=3;
 
 if(RegOpenKey(HKEY_USERS,".Default\\keyboard layout\\toggle",
              &pOpenKey)==ERROR_SUCCESS)
 {
    RegQueryValue(pOpenKey,"",cResult,&lSize);
 
 if(strcmp(cResult,"1")==0)
       KEYBLAY=1;                  // Alt+Shift
 if(strcmp(cResult,"2")==0)
       KEYBLAY=2;                  // Ctrl+Shift
 
 RegCloseKey(pOpenKey);
 }
  else
 MessageBox(0,"Не могу получить данные о способе"
                "переключения раскладки клавиатуры",
                                                      "Внимание!",MB_ICONERROR);
 //------------- Есть ли активный хранитель эрана
 if(!SystemParametersInfo(SPI_GETSCREENSAVEACTIVE,0,&bSCRSAVEACTIVE,0))
          MessageBox(0,"Не могу получить данные об установленном"
               "хранителе экрана", "Внимание!",MB_ICONERROR);
  }
 return 1;
 

Этот код позволяет узнать способ переключения языка и установить факт наличия активного хранителя экрана. Обратите внимание на то, что этот код выполняется только когда библиотека проецируется на адресное пространство процесса - проверяется условие (reason==DLL_PROCESS_ATTACH). Если вас интересуют подробности, то их можно узнать в разделе справки "Win32 Programmer's Reference" в подразделе "DllEntryPoint".

Функция ловушки клавиатуры.

Функция ловушки в общем виде имеет следующий синтаксис:

LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam), где:

HookProc - имя функции,

nCode - код ловушки, его конкретные значения определяются типом ловушки,

wParam, lParam - параметры с информацией о сообщении.

В случае нашей задачи функция должна определять состояние клавиш Alt, Ctrl и Shift (нажаты или отпущены). Информация об этом берётся из параметров wParam и lParam (подробности в "Win32 Programmer's Reference" в подразделе "KeyboardProc"). После определения состояния клавиш надо сравнить его со способом переключения языка (определяется в функции входа). Если текущая комбинация клавиш способна переключить язык, то надо выдать звуковой сигнал.

Всё это реализует примерно такой код:

 LRESULT CALLBACK KeyboardHook(int nCode,WPARAM wParam,LPARAM lParam)
 {        // Ловушка клав. - биканье при перекл. раскладки
 if((lParam>>31)&1)          // Если клавиша нажата...
 switch(wParam)
       {// Определяем какая именно
       case VK_SHIFT: {iShiftKey=UP; break};
       case VK_CONTROL: {iCtrlKey=UP; break};
       case VK_MENU: {iAltKey=UP; break};
 }
 else// Если была отпущена...
 switch(wParam)
 {// Определяем какая именно
       case VK_SHIFT: {iShiftKey=DOWN; break};
       case VK_CONTROL: {iCtrlKey=DOWN; break};
       case VK_MENU: {iAltKey=DOWN; break};
 }
 //--------------
 switch(KEYBLAY)         // В зависимости от способа переключения раскладки
 {
    case 1: // Alt+Shift
 {
 if(iAltKey==DOWN && iShiftKey==UP)
    {
 vfBeep();
      iShiftKey=RESET;
    }
 if(iAltKey==UP && iShiftKey==DOWN)
    {
 vfBeep();
      iAltKey=RESET;
    }
 ((iAltKey==UP && iShiftKey==RESET)||(iAltKey==RESET && 
iShiftKey==UP)) { iAltKey=RESET; iShiftKey=RESET; } break; } //------------------------------------ case 2: // Ctrl+Shift { if(iCtrlKey==DOWN && iShiftKey==UP) { vfBeep(); iShiftKey=RESET; } if(iCtrlKey==UP && iShiftKey==DOWN) { vfBeep(); iCtrlKey=RESET; } if((iCtrlKey==UP && iShiftKey==RESET)||(iCtrlKey==RESET &&
iShiftKey==UP)) { iCtrlKey=RESET; iShiftKey=RESET; } } } return 0; }

Звуковой сигнал выдаётся такой небольшой функцией:

 void vfBeep()
 {// Биканье
 MessageBeep(-1);
 MessageBeep(-1);// Два раза - для отчётливости
 }
 

Функция ловушки мыши.

Эта функция отслеживает движение курсора мыши, получает его координаты и сравнивает их с координатами правого верхнего угла экрана (0,0). Если эти координаты совпадают, то вызывается хранитель экрана. Для отслеживания движения анализируется значение параметра wParam, а для отслеживания координат значение, находящееся в структуре типа MOUSEHOOKSTRUCT, на которую указывает lParam (подробности можно найти в "Win32 Programmer's Reference" в подразделе "MouseProc"). Код, реализующий вышесказанное, примерно такой:

 LRESULT CALLBACK MouseHook(int nCode,WPARAM wParam,LPARAM lParam)
 {       // Ловушка мыши - включает хранитель когда в углу
 if(wParam==WM_MOUSEMOVE || wParam==WM_NCMOUSEMOVE)
 {
  psMouseHook=(MOUSEHOOKSTRUCT*)(lParam);
 if(psMouseHook->pt.x==0 && psMouseHook->pt.y==0)
 if(bSCRSAVEACTIVE)
 PostMessage(psMouseHook->hwnd,WM_SYSCOMMAND,
             SC_SCREENSAVE,0);
 }
 return 0;
 }
 

Обратите внимание, что команда на активизацию хранителя посылается в окно, получающее сообщения от мыши: PostMessage(psMouseHook->hwnd,WM_SYSCOMMAND,SC_SCREENSAVE ,0).

Теперь, когда функции ловушек написаны, надо сделать так, чтобы они были доступны из процессов, подключающих эту библиотеку. Для этого перед функцией входа следует добавить такой код:

 extern "C" __declspec(dllexport) LRESULT CALLBACK KeyboardHook(int,WPARAM,LPARAM);
 extern "C" __declspec(dllexport) LRESULT CALLBACK MouseHook(int,WPARAM,LPARAM);
 

Написание приложения, устанавливающего ловушку.

Создание пустого приложения.

Для создания пустого приложения воспользоваться встроенным мастером. Для этого надо использовать пункт меню File->New: В появившемся окне необходимо выбрать "Console Wizard" и нажать кнопку "Ok". В новом диалоге в разделе "Source Type" следует оставить значение по умолчанию - "C++". Во втором разделе надо снять все флажки. По нажатию "Ок" приложение создаётся.

Создание главного окна.

Следующий этап - это создание главного окна приложения. Сначала надо зарегистрировать класс окна. После этого создать окно (подробности можно найти в "Win32 Programmer's Reference" в подразделах "RegisterClass" и "CreateWindow"). Всё это делает следующий код (описатель окна MainWnd определён глобально):

 BOOL InitApplication(HINSTANCE hinstance,int nCmdShow)
 { // Создание главного окна
 WNDCLASS  wcx; // Класс окна
 wcx.style=NULL;
 wcx.lpfnWndProc=MainWndProc;
 wcx.cbClsExtra=0;
 wcx.cbWndExtra=0;
 wcx.hInstance=hinstance;
 wcx.hIcon=LoadIcon(hinstance,"MAINICON");
 wcx.hCursor=LoadCursor(NULL,IDC_ARROW);
 wcx.hbrBackground=(HBRUSH)(COLOR_APPWORKSPACE);
 wcx.lpszMenuName=NULL;
 wcx.lpszClassName="HookWndClass";
 
 if(RegisterClass(&wcx))                // Регистрируем класс
 {
 MainWnd=CreateWindow("HookWndClass","SSHook",     /* Создаём окно */
         WS_OVERLAPPEDWINDOW,
 CW_USEDEFAULT,CW_USEDEFAULT,
 CW_USEDEFAULT,CW_USEDEFAULT,
 NULL,NULL,hinstance,NULL);
 if(!MainWnd)
 return FALSE;
 
 return TRUE;
 }
    return false;
 }
 

Обратите внимание на то, каким образом был получен значок класса: wcx.hIcon=LoadIcon(hinstance,"MAINICON"); Для того, чтобы это получилось надо включить в проект файл ресурсов (*.res), в котором должен находиться значок с именем "MAINICON".

Это окно никогда не появится на экране, поэтому оно имеет размеры и координаты, устанавливаемые по умолчанию. Оконная процедура такого окна необычайно проста:

   LRESULT CALLBACK MainWndProc(HWND hwnd,UINT uMsg,WPARAM wParam,
         LPARAM lParam)
 {// Оконная процедура
 switch (uMsg)
 {
 case WM_DESTROY:{PostQuitMessage(0); break;}
 //------------
 case MYWM_NOTIFY:
 {
 if(lParam==WM_RBUTTONUP)
 PostQuitMessage(0);
 break; // Правый щелчок на значке - завершаем
 }
 default:
 return DefWindowProc(hwnd,uMsg,wParam,lParam);
 }
 return 0;
 }
 

Размещение значка в системной области.

Возникает естественный вопрос: если окно приложения никогда не появится на экране, то каким образом пользователь может управлять им (например, закрыть)? Для индикации работы приложения и для управления его работой поместим значок в системную область панели задач. Делается это следующей функцией:

 void vfSetTrayIcon(HINSTANCE hInst)
 { // Значок в Tray
 char* pszTip="Хранитель экрана и раскладка";// Это просто Hint
 NotIconD.cbSize=sizeof(NOTIFYICONDATA);
 NotIconD.hWnd=MainWnd;
 NotIconD.uID=IDC_MYICON;
 NotIconD.uFlags=NIF_MESSAGE|NIF_ICON|NIF_TIP;
 NotIconD.uCallbackMessage=MYWM_NOTIFY;
   NotIconD.hIcon=LoadIcon(hInst,"MAINICON");
 lstrcpyn(NotIconD.szTip,pszTip,sizeof(NotIconD.szTip));
 Shell_NotifyIcon(NIM_ADD,&NotIconD);
 }
 

Для корректной работы функции предварительно нужно определить уникальный номер значка (параметр NotIconD.uID) и его сообщение (параметр NotIconD.uCallbackMessage). Делаем это в области определения глобальных переменных:

 #define MYWM_NOTIFY (WM_APP+100)
 #define IDC_MYICON  1006
 
Сообщение значка будет обрабатываться в оконной процедуре главного окна (NotIconD.hWnd=MainWnd):
 case MYWM_NOTIFY:
 {
 if(lParam==WM_RBUTTONUP)
 PostQuitMessage(0);
 break; // Правый щелчок на значке - завершаем
 }
 

Этот код просто завершает работу приложения по щелчку правой кнопкой мыши на значке.

При завершении работы значок надо удалить:

 void vfResetTrayIcon()
 {// Удаляем значок
 Shell_NotifyIcon(NIM_DELETE,&NotIconD);
 }
 
 

Установка и снятие ловушек.

Для получения доступа в функциям ловушки надо определить указатели на эти функции:

 LRESULT CALLBACK (__stdcall *pKeybHook)(int,WPARAM,LPARAM);
 LRESULT CALLBACK (__stdcall *pMouseHook)(int,WPARAM,LPARAM);
 

После этого спроецируем написанную DLL на адресное пространство процесса:

 hLib=LoadLibrary("SSHook.dll");
 
(hLib описан как HINSTANCE hLib).

После этого мы должны получить доступ к функциям ловушек:

 (void*)pKeybHook=GetProcAddress(hLib,"KeyboardHook");
 (void*)pMouseHook=GetProcAddress(hLib,"MouseHook");
 

Теперь всё готово к постановке ловушек. Устанавливаются они с помощью функции SetWindowsHookEx:

 hKeybHook=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)(pKeybHook),hLib,0);
 hMouseHook=SetWindowsHookEx(WH_MOUSE,(HOOKPROC)(pMouseHook), hLib,0);
 (hKeybHook  и hMouseHook описаны как HHOOK hKeybHook; HOOK hMouseHook;)
 

Первый параметр - тип ловушки (в данном случае первая ловушка для клавиатуры, вторая - для мыши). Второй - адрес процедуры ловушки. Третий - описатель DLL-библиотеки. Последний параметр - идентификатор потока, для которого будет установлена ловушка. Если этот параметр равен нулю (как в нашем случае), то ловушка устанавливается для всех потоков.

После установки ловушек они начинают работать. При завершении работы приложения следует их снять и отключить DLL. Делается это так:

 UnhookWindowsHookEx(hKeybHook);
 UnhookWindowsHookEx(hMouseHook);  // Завершаем
 FreeLibrary(hLib);
 

Функция WinMain.

Последний этап - написание функции WinMain в которой будет создаваться главное окно, устанавливаться значок в системную область панели задач, ставиться и сниматься ловушки. Код её должен быть примерно такой:

 WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine,
    int nCmdShow)
 {
 MSG msg;
 //----------------
 hLib=LoadLibrary("SSHook.dll");
 if(hLib)
 {
 (void*)pKeybHook=GetProcAddress(hLib,"KeyboardHook");
 hKeybHook=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)(pKeybHook),
    hLib,0);// Ставим ловушки
 (void*)pMouseHook=GetProcAddress(hLib,"MouseHook");
 hMouseHook=SetWindowsHookEx(WH_MOUSE,(HOOKPROC)(pMouseHook),
  hLib,0);
 //-------------------------------
 if (InitApplication(hInstance,nCmdShow))// Если создали главное окно
 {
 vfSetTrayIcon(hInstance);// Установили значок
 while (GetMessage(&msg,(HWND)(NULL),0,0))
 {// Цикл обработки сообщений
 TranslateMessage(&msg);
 DispatchMessage(&msg);
 }
 //---------------------------------- Всё - финал
 UnhookWindowsHookEx(hKeybHook); // Снимаем ловушки
 UnhookWindowsHookEx(hMouseHook);
 FreeLibrary(hLib);// Отключаем DLL
 vfResetTrayIcon();// Удаляем значок
 return 0;
 }
 }
 return 1;
 }
 
 

После написания этой функции можно смело запускать полностью готовое приложение.

Полный исходный код программы

 

<< ВЕРНУТЬСЯ В ПОДРАЗДЕЛ

<< ВЕРНУТЬСЯ В ОГЛАВЛЕНИЕ




Материалы находятся на сайте https://exelab.ru/pro/



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS