Впровадження DLL за допомогою пасток

Underground InformatioN Center

1. Навіщо

  
Відповідь досить проста: якщо Вам необхідно вплинути на віддалений процес, а точніше на потік
у віддаленому процесі, то доведеться використовувати який-небудь механізм міжпроцесної зв'язку. Так
як цільовий потік може і не містити коду, призначеного для взаємодії з вашим
потоком, то доведеться використовувати спеціальні засоби, одним з яких і є
хуки (пастки). За допомогою пастки ви повідомляє ОС про необхідність
впровадження DLL в адресний простір потоку, для якого встановлена пастка.

  
Пастка встановлюється функцією SetWindowsHookEx, яка з усіма параметрами описана в MSDN.
Я хочу перейти відразу до практичної сторони питання, тому за теорією звертайтеся до MSDN або
прочитайте статтю "хуки в Win32", посилання вказана в кінці. Якщо комусь мало, пишіть мені і я перетворю
ваш HDD в суцільну базу даних по хукам.

2. Предмет

  
Для ілюстрації цього методу я вирішив написати програму, що зберігає позиції ярликів на
десктопі. Щось подібне описано у Ріхтера, наприклад функції, що відповідають за взаємодію
з елементом управління "графічний список" десктопа, майже ідентичні, так як використовується
стандартний алгоритм. Оскільки більшість віконних повідомлень для стандартних елементів
управління не можуть подолати кордон процесу, то доводиться, як кажуть, діставати
підборіддям до ліктя. Коротше, якщо Магомет не йде до гори, то нам доведеться впровадити DLL в
потік explorer, що відповідає за роботу з ярликами, назвемо його умовно ListBox. Встановивши
пастку на потік ListBox, ми впровадимо свою DLL в його адресний простір і встановимо
функцію-хук, щоразу спричинюється системою в залежності від умови хука. Я буду
використовувати хук WH_GETMESSAGE – він змусить систему викликати нашу функцію обробки хука
тоді, коли в черзі потоку з'явиться повідомлення. Далі під час першого виклику функція просто
створює вікно діалогу і призначає його віконної процедурою функцію з впровадженої DLL.
Таким чином у нашому потоці ми зможемо взаємодіяти з ListBox допомогою створеного
вікна діалогу. Синхронізація викликає потоку і діалогової процедури в потоці ListBox
виробляється за допомогою об'єкта ядра "подія", яке звільняється, як тільки створене
вікно діалогу готове до обробки вхідних повідомлень. У лістингах все дуже детально
описано – тому дивіться листинги і нехай скомпіліт їх ваш комп :-)).

3. Лістинг № 1 – модуль викликає процесу

/ / "InjectDllH.cpp": Defines the entry point for the application.

#include "stdafx.h"
__declspec (dllimport) BOOL WINAPI InjectDLL (DWORD dwThreadId, HANDLE
SwitcherEvent);
////////////////////////////////////////////////// //////////////
BOOL __stdcall DlgProc (HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
  HWND hwnd;
  static HWND hwndDSPS,hwndLV,hList;
  HANDLE SwitcherEvent;
  char *buf;
  UINT action;
  switch(iMsg){
  case WM_INITDIALOG:   
    hList=GetDlgItem(hWnd,IDC_EVENTS_HANDLER);
    return TRUE;
    case WM_APP:
      if(lParam==IDC_SAVE)
/ / Отримано повідомлення від сервера, про те що пішло збереження
/ / Позицій
        SendMessage(hList,LB_ADDSTRING,0,(LPARAM)"Saving in
                                              process....");
	  else
//-||-||- Пішло відновлення
        SendMessage(hList,LB_ADDSTRING,0,(LPARAM)"Restoring in
                                                 process....");
      break;
	case WM_COMMAND:
    switch(LOWORD(wParam)){
case IDC_SAVE: action = IDC_SAVE; buf = "Shortcuts were saved ...";
                                            goto processing;  
      case IDC_RESTORE:action=IDC_RESTORE;buf="Shortcuts were
restored ...";
    processing:	
	SendMessage(hList,LB_RESETCONTENT ,0,0);
/ / Отримати описувач елемента управління "графічний список"
/ / Десктопа
hwndLV = GetFirstChild (GetFirstChild (FindWindow (__TEXT ("ProgMan"),
NULL)));
    if(!IsWindow(hwndLV)) {
SendMessage (hList, LB_ADDSTRING, 0, (LPARAM) "Error getting explorer 
handle ");
       goto exit;
    }
SendMessage (hList, LB_ADDSTRING, 0, (LPARAM) "Processing ......");
/ / Створюємо об'єкт-подія, за допомогою якого ми будемо
/ / Синхронізувати наш процес з серверним
    SwitcherEvent=CreateEvent(NULL,TRUE,TRUE,"bullet");
	if(SwitcherEvent==NULL) {
      _INFO("Uppsss..","Error creating switcher event!!");
    goto exit;
	}
    SendMessage(hList,LB_ADDSTRING,0,(LPARAM)"Creating event
                                               object....OK");

/ / Встановлюємо пастку, яка запровадить нашу DLL в Експлорер.
/ / Таким чином ми зможемо посилати повідомлення нашої DLL, а вона
/ / Пошле їх елементу управління "графічний список" десктопа, так
/ / Як повідомлення для стандартних елементів управління в більшості
/ / Здебільшого не можуть подолати межі процесів. Тому необхідно
/ / Впровадитися в процес, якому належить елемент і через
/ / Механізм повідомлень управляти впровадженої функцією, яка в свою
/ / Чергу буде управляти елементом. А для того щоб можна було
/ / Послати повідомлення цієї функції, необхідно пов'язати з нею чергу
/ / Повідомлень - це можливо шляхом створення вікна, тоді процедура
/ / Цього вікна і буде обробляти наші повідомлення. Тобто: ми
/ / Впроваджуємо DLL, функція хука створює вікно з віконною процедурою,
/ / Розташованої у впровадженій DLL, ця програма обробляє наші
/ / Повідомлення, вона є, по суті, перенаправленням між нашим
/ / Процесом і елементом управління "графічний список".
InjectDLL (GetWindowThreadProcessId (hwndLV, NULL), SwitcherEvent);
SendMessage (hList, LB_ADDSTRING, 0, (LPARAM) "Injecting dll into
Explorer .... OK ");
SendMessage (hList, LB_ADDSTRING, 0, (LPARAM) "Waiting for GetMessage
Proc .... got ");
/ / Очікуємо поки звільниться об'єкт-подія, сервер звільняє його,
/ / Коли вікно діалогу успішно створено і очікує повідомлень
if (WaitForSingleObject (SwitcherEvent, INFINITE) == WAIT_FAILED) {
       _INFO("Err...","Something is wrong with server...");
       goto exit;
    }
/ / Отримати описувач діалогу, створеного сервером
hwndDSPS = FindWindow (NULL, __TEXT ("bullet server instance"));
/ / Перевіряємо валідність описувача
    if(IsWindow(hwndDSPS)) {
SendMessage (hList, LB_ADDSTRING, 0, (LPARAM) "Checking for server
window .... OK ");
/ / Даємо команду процедурі діалогу, впровадженої в explorer,
/ / Почати роботу з ListBox, ідентифікованим за hwndLV,
action == SAVE / RESTORE
SendMessage (hwndDSPS, WM_APP, (WPARAM) hwndLV, (LPARAM) action);
       SendMessage(hList,LB_ADDSTRING,0,(LPARAM)buf);
SendMessage (hList, LB_ADDSTRING, 0, (LPARAM) "Shutdowning server ....");

/ / Знищити вікно діалогу після завершення операції
/ / Збереження / відновлення. Використовуємо SendMessage, так як ми
/ / Повинні бути впевнені, що вікно діалогу буде знищено до зняття
/ / Хука, інакше відбудеться порушення доступу до пам'яті. Діалог отримає
/ / Повідомлення і ОС передасть управління віконної процедури,
/ / Розташованої у впровадженій DLL. Але після зняття хука ця DLL
/ / Буде відключена від процесу->> error.
       SendMessage(hwndDSPS, WM_CLOSE, 0, 0);
/ / Переконуємося, що діалог знищений
       if(!IsWindow(hwndDSPS)) {
SendMessage (hList, LB_ADDSTRING, 0, (LPARAM) "Server stopped!
Releasing hook ");
/ / Зняти хук і прибрати віконну процедуру серверного діалогу з
          // explorer
          InjectDLL(0,0);
       }
    }
    exit:   return TRUE;
    case IDC_EXIT:
    SendMessage(hList,LB_ADDSTRING,0,(LPARAM)"Quitting...");
    EndDialog(hWnd,LOWORD(wParam));
    return TRUE;
    }
    break;
    }
return FALSE;
}

///////////////////////////////////////////////////////////////
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
/ / Запуск діалогу
int choice = DialogBox (hInstance, MAKEINTRESOURCE (IDD_CALLER_DIALOG),
NULL, DlgProc);
   return 0;
}

  
Я думаю, що тут все прозоро, тому перейду відразу до коду DLL. Там всі також у коментарях,
але пару слів я скажу:
Функція InjectDLL встановлює хук і відправляє повідомлення explorer для активації нашої DLL.
ОС відразу впроваджує її в адресний простір потоку ListBox. Також InjectDLL встановлює
об'єкт "подія" у стан ЗАЙНЯТО, викликає потік буде чекати до тих пір, поки він не
звільниться. Далі викликається GetMsgProc, функція обробки хука. Вона створює вікно діалогу
і при успішному його створенні встановлює об'єкт "подія" у вільний стан. Тоді
викликає процес починає продукувати команди вікна діалогу для їх пересилання потоку
ListBox. Ну а тепер source:

4. Лістинг № 2 – модуль впроваджуваної DLL

/ / DLLToInject.cpp: Defines the entry point for the DLL application.

#include "stdafx.h"

/ / Визначаємо змінні, доступні всім екземплярам даної DLL
#pragma data_seg("Shared")
HHOOK g_hhook = NULL;
/ / Ідентифікатор хука. Він необхідний для виклику функції СallNextHookEx,
/ / А так як виклик SetWindowsHookEx виробляється в процес, упроваджуємо
/ / DLL, то даний ідентифікатор можливо передати у функцію,
/ / Обробну хук, тільки таким чином
#pragma data_seg()

/ / Директива лінкера створити поділювану (shared) секцію з атрибутами RWS
#pragma comment(linker, "/section:Shared,rws")


HINSTANCE g_hinstDll = NULL;
const TCHAR g_szRegSubKey[] = 
__TEXT ("Software \ \ buLLet \ \ Desktop Shortcuts Position Saver");


void SaveListViewItemPositions (HWND hwndLV); / / зберігає ярлики
void RestoreListViewItemPositions (HWND hwndLV); / / відновлює ярлики
BOOL CALLBACK DlgProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
/ / Процедура діалогу серверного вікна

void OnClose(HWND hwnd);
LRESULT CALLBACK GetMsgProc (int nCode, WPARAM wParam, LPARAM lParam);
/ / Обробляє хук
///////////////////////////////////////////////////////////////
__declspec(dllexport) BOOL WINAPI InjectDLL(DWORD dwThreadId,
HANDLE SwitcherEvent) {
BOOL fOk = FALSE;
if (dwThreadId != 0) {
/ / Перевіряємо чи не встановлений хук???
  if(g_hhook != NULL)
    return fOk;
/ / Якщо так, то вихід
    ResetEvent(SwitcherEvent);
/ / Встановлюємо хук на зазначений у dwThreadId потік
g_hhook = SetWindowsHookEx (WH_GETMESSAGE, GetMsgProc, g_hinstDll,
dwThreadId);
    fOk = (g_hhook != NULL);
    if (fOk) 
/ / Пастка успішно впроваджена, тепер викликаємо PostThreadMessage, щоб
/ / Послати explorer повідомлення, тоді ОС автоматично впроваджує нашу
/ / DLL в адресний простір explorer і викликає GetMsgProc
      fOk = PostThreadMessage(dwThreadId, WM_NULL, 0, 0);
} 
else {
/ / Перевіряємо чи встановлений хук????
   if(g_hhook == NULL)
	  return FALSE;
   fOk = UnhookWindowsHookEx(g_hhook);
/ / Відключаємо DLL від explorer
   g_hhook = NULL;
}

   return(fOk);
}

////////////////////////////////////////////////// //////////////
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
 switch (ul_reason_for_call) {

   case DLL_PROCESS_ATTACH:
/ / DLL прив'язана до якогось потоку
     g_hinstDll = (HINSTANCE)hModule;
     break;

   case DLL_THREAD_ATTACH:
     // A new thread is being created
     // in the current process
     break;

   case DLL_THREAD_DETACH:
     // A thread is exiting cleanly
     break;

   case DLL_PROCESS_DETACH:
     // The calling process is detaching the
     // DLL from its address space
     break;
   }
   return TRUE;
}
//////////////////////////////////////////////////////////////
LRESULT CALLBACK GetMsgProc (int nCode, 
   WPARAM wParam, LPARAM lParam) 
{
   static BOOL fFirstTime = TRUE;
   HWND hDlg;
   HANDLE SwitcherEvent;

   if (fFirstTime) {
/ / DLL тільки що була впроваджена в explorer
      fFirstTime = FALSE;
/ / Відкриваємо об'єкт-подія для синхронізації із зухвалим
/ / Процесом
SwitcherEvent = OpenEvent (EVENT_MODIFY_STATE, FALSE, "bullet");
      if(SwitcherEvent==NULL)
      _INFO("Uppsss..","Error opening switcher event!!");
/ / Створюємо приховане вікно діалогу для можливості обміну повідомленнями
/ / Із зухвалим процесом
hDlg = CreateDialog (g_hinstDll, MAKEINTRESOURCE (IDD_SERVER_DIALOG),
NULL, DlgProc);
      if(IsWindow(hDlg)) {
/ / Повідомляємо зухвалому процесу, що діалог створений і готовий
/ / Приймати команди
      SetEvent(SwitcherEvent);
      }
   }
return (CallNextHookEx(g_hhook, nCode, wParam, lParam));
}
/////////////////////////////////////////////////////////////
BOOL CALLBACK DlgProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch (uMsg) {
    case WM_CLOSE: 
       OnClose(hwnd);
       break;
    case WM_APP:
       HWND hwndLV,hwndDSPS;
hwndDSPS = FindWindow (NULL, __TEXT ("buLLet shortcut saver"));
       if (lParam==IDC_SAVE) {
          SendMessage(hwndDSPS,WM_APP,0,(LPARAM)IDC_SAVE);
/ / Повідомити, що збереження пішло
          SaveListViewItemPositions((HWND) wParam);
/ / Зберегти позиції
       }
       if (lParam==IDC_RESTORE) {
          SendMessage(hwndDSPS, WM_APP, 0,(LPARAM)IDC_RESTORE);
/ / Повідомити, що відновлення пішло
          RestoreListViewItemPositions((HWND) wParam);
/ / Відновити ярлики
        }
        break;
  }
  return(FALSE);
}
///////////////////////////////////////////////////////////////
void OnClose(HWND hwnd) {
   DestroyWindow(hwnd);
}
////////////////////////////////////////////////// //////////////
void SaveListViewItemPositions(HWND hwndLV) {
   int nItem, nMaxItems = ListView_GetItemCount(hwndLV);
   HKEY hkey;
   DWORD dwDisposition;
   LONG l = RegDeleteKey(HKEY_CURRENT_USER, g_szRegSubKey);
   l = RegCreateKeyEx(HKEY_CURRENT_USER, g_szRegSubKey,
      0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, 
      NULL, &hkey, &dwDisposition);
   for (nItem = 0; nItem <NMaxItems; nItem + +) {TCHAR szName [1024]; POINT pt; ListView_GetItemText (hwndLV, nItem, 0, szName, 1024); ListView_GetItemPosition (hwndLV, nItem, & pt); l = RegSetValueEx (hkey, szName, 0, REG_BINARY, ( PBYTE) & pt, sizeof (pt)); if (l! = ERROR_SUCCESS) _INFO ("OOooppss...","RegSetValueEX return ERROR");} RegCloseKey (hkey);} ///////////////////////////////////////// //////////////////////// void RestoreListViewItemPositions (HWND hwndLV) {HKEY hkey; LONG l = RegOpenKeyEx (HKEY_CURRENT_USER, g_szRegSubKey, 0, KEY_QUERY_VALUE, & hkey); if (l == ERROR_SUCCESS) {int nIndex; DWORD dwStyle = (DWORD) GetWindowLong (hwndLV, GWL_STYLE); if (dwStyle & LVS_AUTOARRANGE) SetWindowLong (hwndLV, GWL_STYLE, dwStyle & ~ LVS_AUTOARRANGE); l = NO_ERROR; for (nIndex = 0; l! = ERROR_NO_MORE_ITEMS; nIndex + +) {TCHAR szName [1024]; POINT pt; LV_FINDINFO lvfi; unsigned long cbValueName = 1024; LPDWORD lpcbValueName = & cbValueName; unsigned long cbData = sizeof (pt), nItem; LPDWORD lpcbData = &cbData; DWORD dwType; l = RegEnumValue (hkey, nIndex, szName, lpcbValueName, NULL, & dwType, (PBYTE) & pt, lpcbData); if (l == ERROR_NO_MORE_ITEMS) continue; if ((dwType == REG_BINARY) & & (cbData == sizeof ( pt))) {lvfi.flags = LVFI_STRING; lvfi.psz = szName; nItem = ListView_FindItem (hwndLV, -1, & lvfi); if (nItem! = -1) {ListView_SetItemPosition (hwndLV, nItem, pt.x, pt. y);}}} SetWindowLong (hwndLV, GWL_STYLE, dwStyle); RegCloseKey (hkey);}} 

Ну ось ніби і все. Буду радий будь-яким запропонованим удосконаленням. Також із задоволенням
прийму всі поради та доповнення. Ну і звичайно з готовністю вислухаю будь-яку критику, тільки
вагою не більше 100 Кб :-)) Пишіть!

Також додається повний проект для Visual C + + 6.0. Завантажити
тут
(Завантажено 383 раз)
Завантажити статтю "хуки в Win32" тут

buLLet
bullet@atlasua.net
uinC Member
[c]uinC

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*