Underground InformatioN Center [&articles] 
[network & security news] [RSS & Twitter] [articles, programing info] [books] [links, soft & more...] [soft archive][home]

Внедрение DLL с помощью ловушек

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. Скачать здесь (скачано 9229 раз)
Скачать статью "Хуки в Win32" здесь

buLLet
bullet@uinc.ru
uinC Member
[c]uinC

Все документы и программы на этом сайте собраны ТОЛЬКО для образовательных целей, мы не отвечаем ни за какие последствия, которые имели место как следствие использования этих материалов\программ. Вы используете все вышеперечисленное на свой страх и риск.

Любые материалы с этого сайта не могут быть скопированы без разрешения автора или администрации.


[network & security news] [RSS & Twitter] [articles, programing info] [books] [links, soft & more...] [soft archive][home]
 Underground InformatioN Center [&articles] 
2000-2015 © uinC Team