ROOTKITS.SU
Здравствуйте, Гость  
Главная
Новости
Программы
Статьи
Ссылки друзей
Форум
Хостинг
Вход на сайт
Логин 
Пароль 
Последние темы на форуме
21.06.2010 19:41:45 — Exploiting Online Games: Cheating Massively Distributed Systems ZUNAMI (0)
21.05.2010 9:44:45 — Настройка отладочного комплекса на базе WMWare red mould (2)
10.05.2010 20:50:41 — нужна помощь с copyhook в Delphi 7 star007 (0)
29.04.2010 19:40:17 — PAGE_FAULT_IN_NONPAGED_AREA kraby (6)
29.04.2010 0:47:56 — Отладочные символы и Windbg Asm0Dey (1)
15.03.2010 23:12:51 — Выбор метдов скрытия процесса. Asm0Dey (6)
19.02.2010 23:26:25 — XP->Vista. Сплайсинг. kraby (4)
Статьи
Правда о KeUserModeCallback()
Это даже не статья, а маленькая заметочка. Заметочка на тему избитой всеми функции KeUserModeCallback().
Автор 
Оконные хуки: взгляд изнутри
В последнее время на форумах все чаще стали проскальзывать вопросы о том, как получить список оконных хуков, установленных на события мыши или клавиатуры... Автор статьи - Twister.
Автор 
«Сырая» загрузка драйвера
Загрузка драйвера с использованием native-функции NtLoadDriver(…)
Автор 
Инжект: лезем через окно
Одним из самых соблазнительных мест для преступника, пытающегося пробраться в чужую квартиру, безусловно, является окно...
Автор 
Все статьи | Добавить статью

Оконные хуки: взгляд изнутри
В последнее время на форумах все чаще стали проскальзывать вопросы о том, как получить список оконных хуков, установленных на события мыши или клавиатуры... Автор статьи - Twister.
   В последнее время на форумах все чаще стали проскальзывать вопросы о том, как получить список оконных хуков, установленных на события мыши или клавиатуры.  Уметь их перечислять и идентифицировать было бы довольно полезно, учитывая обилие различных видов spyware в наши дни. И хотя кейлогеры aka клавиатурные шпионы, работающие в режиме пользователя с помощью этих самых хуков, потихоньку уступают свое место драйверам-фильтрам, тема эта все еще продолжает оставаться актуальной. К сожалению, сегодня не многие представители security-софта несут в себе такой функционал, как анализ оконных хуков. До написания кода к этой статье мне была известна лишь одна такая утилита - китайский IceSword. Он перечисляет хуки, но не снимает их и не знает, в какой библиотеке dll находится обработчик «ловушки». Да и под Windows Vista этот инструмент не работает.
   Надо заметить, что информации по этому вопросу в сети не так уж и много. По крайней мере я не нашел ни одного топика или, тем более, статьи, где давался бы исчерпывающий ответ. На форуме wasm.ru была замечена тема, в которой некто DelExe вкратце объяснял, как выудить нужную нам информацию, но его код по определенным причинам был непереносим и работал только с двумя версиями Windows, да и описания недокументированных структур были в корне не верны. Остальные найденные материалы даже упоминать не буду, настолько скудными они оказались. Вот я и подумал, что неплохо было бы немного шире осветить проблему оконных хуков, и решил внести свою лепту.
   Думаю, не стоит излагать здесь о хуках то, что можно с лёгкостью найти в msdn или остальном интернете и поэтому начну непосредственно с того, где и как нам их найти... Если приглядеться к дескрипторам хуков и вспомнить, что они глобальны, то можно заметить их поразительную схожесть с дескрипторами окон. А учитывая тот факт, что это элементы одной gui-подсистемы, мы имеем все права предположить - хэндлы окон и хуков лежат в одной таблице. А ведь и, правда, вы не встретите одновременно окно и хук с одинаковыми хэндлами. В прошлой своей статье (http://wasm.ru/article.php?article=window_inject) я рассказывал, как находятся структуры оконных дескрипторов, и сейчас мы пойдем тем же путем.
   Где-то в адресном пространстве каждого gui-процесса лежит важная структура, именуемая SHAREDINFO. Она общая для всех, но может быть спроецирована по разным адресам в памяти. Поэтому система хранит указатель на нее в не экспортируемой из user32.dll переменной gSharedInfo. Благо со времен предыдущей статьи нашелся способ отыскать gSharedInfo динамически, а не хранить жёстко прописанные адреса в коде для каждой версии ОС. Способ основан на поиске по сигнатурам программного кода примерно следующего вида. Код этот находится в конце процедуры UserRegisterWowHandlers():
.text:77D735D2                 mov     dword ptr [eax+44h], offset _GetFullUserHandle@4
.text:77D735D9                 mov     dword ptr [eax+48h], offset _NtUserGetMenuIndex@8
.text:77D735E0                 mov     dword ptr [eax+4Ch], offset _WowGetDefWindowProcBits@
.text:77D735E7                 mov     dword ptr [eax+50h], offset _NtUserFillWindow@16
.text:77D735EE                 mov     dword ptr [eax+54h], offset _aiClassWow
.text:77D735F5                 mov     eax, offset _gSharedInfo
.text:77D735FA                 pop     ebp
.text:77D735FB                 retn    8

   Вся фишка в том, что во всех проверенных мной версиях системы от win_2k до win_vista инструкция «mov     eax, offset _gSharedInfo» всегда располагается за последней инструкцией «mov     dword ptr [eax+xxh], …». А вот и функция для автоматического нахождения gSharedInfo:
function GetSharedInfoAddress(): PSHAREDINFO;
var
  WowHandlers: pointer;
  i: DWORD;
begin
result := nil;
WowHandlers := GetProcAddress(GetModuleHandle('user32.dll'), 'UserRegisterWowHandlers');
if WowHandlers <> nil then
  for i := DWORD(WowHandlers) to DWORD(WowHandlers) + $1000 do
    if (WORD(pointer(i)^) = WORD($40C7)) and (BYTE(pointer(i + 7)^) = BYTE($B8)) then
      begin
      result := pointer(DWORD(pointer(i + 8)^));
      exit;
      end;
end;

   Итак, указатель добывать научились, пора познакомиться с самой структурой SHAREDINFO:
PSHAREDINFO = ^SHAREDINFO;
    SHAREDINFO = packed record
      psi: pointer;
      aheList: PHANDLEENTRY_ARRAY;
      pDispInfo: pointer;
      ulSharedDelta: DWORD;
      end;

   Нас интересует ее второй элемент под названием aheList. Это указатель на массив структур HANDLEENTRY, каждая из которых, кроме первой, неиспользуемой, описывает соответствующий gui-объект в ядре. В этом массиве лежат описатели всех окон, меню, хуков. Кстати, младшее слово хэндла окна или хука есть не что иное, как индекс соответствующей структуры в этом массиве. К сожалению, мне не удалось отыскать то место, где хранится текущая размерность (кол-во элементов) массива, поэтому для перебора мы будем использовать цикл от нуля до 0x0000FFFF – 1 (константа HMINDEXBITS, содержит максимально допустимый индекс в массиве) и ловить исключение при попытке доступа к невыделенной памяти. Давайте посмотрим на HANDLEENTRY поближе:
PHANDLEENTRY = ^HANDLEENTRY;
    HANDLEENTRY = packed record
      pHead: pointer;
      pOwner: pointer;
      bType: BYTE;
      bFlags: BYTE;
      wUniq: WORD;
      end;

   pOwner указывает на родительский объект. Это может быть W32THREAD или W32PROCESS, но о них позже. bType отражает тип хэндла и содержит константу TYPE_HOOK (0x05) в случае, если HANDLEENTRY описывает хук. bFlags держит в себе информацию о текущем состоянии хэндла (закрыт, в процессе закрытия и т.д.) и большую часть времени обнулен. Что-то мне подсказывает, что это поле используется системой не так активно, как изначально задумывали разработчики. Хотя если ради эксперимента установить bFlags равным HANDLEF_DESTROY (0x01), то через некоторое время структура будет автоматически стерта из массива и хэндл станет невалиден. wUniq в ранних версиях ОС должен был совпадать со старшим словом хэндла, но в Windows Vista это не актуально, так что можно забыть про этот элемент. Самое интересное я оставил напоследок, это поле pHead. Оно содержит указатель на объект в ядре, в случае с хуком на tagHOOK:
PHOOK = ^HOOK;
    tagHOOK = packed record
      head: THRDESKHEAD;
      phkNext: PHOOK;
      iHook: integer;
      offPfn: DWORD;
      flags: DWORD;
      ihmod: integer;
      ptiHooked: pointer;
      rpdesk: pointer;
      end;
    HOOK = tagHOOK;

   Имея эту структуру, мы можем выудить такую информацию о хуке, какую только возможно. Но начнем по порядку. Поле head это стандартный заголовок объектов такого типа, он встречается и в оконной структуре tagWND и в других местах. Мы вернемся к нему чуть позже, так как в этом заголовке хранятся кое-какие важные для нас данные. phkNext содержит указатель на следующий хук в цепочке. Именно на этот элемент опирается работа функции CallNextHookEx(). iHook говорит о том, какого типа хук – WH_CBT, WH_KEYBOARD или другого. Полный список типов есть в msdn.  offPfn в случае локального хука представляет собой полный адрес callback-обработчика событий в АП соответствующего процесса. В случае же глобального хука offPfn содержит смещение обработчика относительно базового адреса загрузки dll, так как этот адрес может разниться от процесса к процессу. Мы ведь помним, что для того, чтобы фильтровать сообщения во всех процессах, система должна подгрузить dll с обработчиком фильтра в каждый из них. К слову скажу, что библиотека подгружается не сразу во все процессы, а при обработке хука функцией CallNextHookEx() или DispatchMessage(), в случае если она не была подгружена ранее. ptiHooked содержит указатель на структуру W32THREAD, описывающую «похуканный» поток, но только в том случае, если хук локален. Если же хук глобален, ptiHooked содержит ноль, однако мы все равно можем «достучаться» до потока, создавшего хук. В этом нам поможет заголовок head, который описывается структурой типа THRDESKHEAD  – нужный нам указатель лежит в head.throbjhead.pti:
PTHRDESKHEAD = ^THRDESKHEAD;
    THRDESKHEAD = packed record
      throbjhead: THROBJHEAD;
      deskhead: DESKHEAD;
      end;
PTHROBJHEAD = ^THROBJHEAD;
    THROBJHEAD = packed record
      head: HEAD;
      pti: pointer;                   // То, что нам нужно – указатель на W32THREAD потока-создателя
      end;
PHEAD = ^HEAD;
    HEAD = packed record
      h: DWORD;                       // Хэндл объекта
      cLockObj: DWORD;
      end;

  PDESKHEAD = ^DESKHEAD;
    DESKHEAD = packed record
      rpdesk: pointer;                // PDESKTOP
      pSelf: pointer;
      end;

   Из структуры W32THREAD легко получить указатель на ETHREAD потока, так как он всегда является ее первым элементом, а от туда Id потока и процесса, точнее из CLIENT_ID. Описание самой структуры я приводить не буду - оно очень сильно разнится от версии к версии. Так же небольшой дискомфорт доставляет то, что смещение CLIENT_ID в ETHREAD тоже «гуляет» на разных системах, но сей незамысловатый код, суть которого, я думаю, разъяснять не нужно, легко справляется с этой неувязкой:
ULONG
GetClientIdOffset()
{
      PCWSTR            pFuncName = L"PsGetCurrentThreadId";
      UNICODE_STRING    usFuncName;
      PVOID             pFuncAddr;
      ULONG             result = 0;

      RtlInitUnicodeString(&usFuncName, pFuncName);
      pFuncAddr = MmGetSystemRoutineAddress(&usFuncName);
      if (pFuncAddr) {
            result = *(PULONG)((ULONG)pFuncAddr + 8);
            }

      return result - 4;
}

   Самый интересный элемент в tagHOOK это ihmod. Его я не зря затронул последним. Если ihmod равен -1, то хук локален. Если же нет, то это индекс в одном загадочном, нигде не описанном и не экспортируемом массиве, имя которому aatomSysLoaded.  Состоит он предположительно из 60-ти элементов, каждый из которых размером в слово и представляет собой глобальный атом из UserAtomTable. Как мы знаем, существует две таблицы глобальных атомов – одна ntoskrnl, другая win32k. С первой работают такие функции, как NtAddAtom(), NtFindAtom() и другие. Со второй, которая UserAtomTable, в основном работают внутренние функции win32k. Так вот, в массиве aatomSysLoaded хранятся атомы, значением каждого из которых является wide-строка с полным путем к dll, содержащей обработчик хука. Теперь все упирается в то, как найти в памяти этот самый массив. Именно на этом моменте споткнулся вышеупомянутый DelExe, он не нашел метода отыскать aatomSysLoaded динамически и хранил в коде адреса, верные лишь для win 2k sp2 и win xp sp1. Надо заметить, это довольно странно, так как поиск не представляет собой ни чего сложного. К примеру, в win32k.sys от Windows XP SP1 этот массив «упоминается» целых 25 раз! Мое внимание сразу привлек следующий код:
.text:BF82CE6A                 cmp     aatomSysLoaded[esi*2], di
.text:BF82CE72                 jz      short loc_BF82CE79

   Дело в том, что инструкция типа «cmp     some_addr[esi*2], di» встречается в win32k лишь единожды. Причем это справедливо абсолютно для всех версий линейки NT начиная от w2k! Однако мне не хотелось делать поиск всего лишь по одной инструкции, и я начал искать еще одну, на которую можно было бы сделать дополнительный упор, для подстраховки, так сказать. Долго искать не пришлось. Обратите внимание на вторую строчку приведенного выше дизассемблерного листинга. Это JZ – условный прыжок на 5 байт вперед. Он так же присутствует везде и стоит всегда на своем месте. Правда, по ходу тестирования на разных системах, в которое свой неоценимый вклад внес небезызвестный EP_X0FF, был обнаружен единственный билд win32k, в котором на месте JZ находился JNZ с прыжком на 0x1C байт вперед. Номер этого билда - 5.0.2195.6708 (Windows 2000). Итак, учитывая все вышеизложенное, можно смело «стряпать» универсальный код поиска aatomSysLoaded, который будет работать везде:
ULONG
GetaatomSysLoadedAddress()
{
      ULONG      i, result = 0;
      PVOID      Win32kBase = GetWin32kBase();

      if (Win32kBase) {
      for (i = (ULONG)Win32kBase + 0x1000; i < (ULONG)Win32kBase + 0x000FFFFF; i++) {
            if ( (*(ULONG*)i == 0x753C3966)
               ( (*(SHORT*)(i + 8) == 0x0574) ||
               (*(SHORT*)(i + 8) == 0x1C75) ) ) {
                        result = *(ULONG*)(i + 4);
                        break;
                        }
                  }
            }

      return result;
}

   Думаю, стоит упомянуть, что выполнение данного кода возможно лишь в контексте gui-процесса, поэтому если контекст заранее неизвестен, то следует приаттачиться к нужному процессу с помощью KeStackAttachProcess(), например к csrss.exe – он всегда присутствует в системе. После того, как aatomSysLoaded найден, мы уже можем найти нужный нам атом, связанный с конкретным хуком:
Atom = aatomSysLoaded[Hook.ihmod];
   Дальше можно получить полный путь к dll. На самом низком уровне это реализуется с помощью недокументированного экспорта ntoskrnl.exe, функции RtlQueryAtomInAtomTable():
NTSTATUS
RtlQueryAtomInAtomTable(
    __in PVOID AtomTableHandle,
    __in RTL_ATOM Atom,
    __out_opt PULONG AtomUsage,
    __out_opt PULONG AtomFlags,
    __inout_bcount_part_opt(*AtomNameLength, *AtomNameLength) PWSTR AtomName,
    __inout_opt PULONG AtomNameLength
    )

   Но нам не известно значение UserAtomTableHandle, которое надлежит передать в функцию, а поиск его – лишний геморрой. Не будем усложнять себе жизнь и пойдем более простым путем. В драйвере win32k существует внутренняя функция UserGetAtomName(). Сама она не экспортируется, зато есть известные функции, активно ее использующие. Это NtUserGetClipboardFormatName() и NtUserGetAtomName(). Обе две они присутствуют в Shadow SSDT, и именно с их помощью мы достигнем цели:
W32KAPI
int
NtUserGetClipboardFormatName(
    IN UINT format,
    OUT LPWSTR lpszFormatName,
    IN UINT chMax);

   Описания NtUserGetAtomName() я ни где не нашел, но установил, что у нее всего два параметра, по назначению эквивалентные первым двум параметрам NtUserGetClipboardFormatName(). Обе эти функции можно использовать и в ядре, и в юзермоде - user32.dll с успехом это делает. В случае успешного вызова искомый нами путь к dll запишется по адресу, указанному в lpszFormatName. Если же вы хотите свести работу с недокументированными функциями к минимуму, то в юзермоде можно использовать GetClipboardFormatNameW(), как я и сделал (смотрите пример, приложенный к статье):
GetClipboardFormatNameW(Atom, @dllPath, 256);
   Собственно говоря, на этом вся теория заканчивается. Теперь у нас достаточно информации, чтобы перечислить и идентифицировать все установленные в системе хуки. К статье приложен пример, который на практике реализует все вышеизложенное – в случае, если что-то окажется непонятным, вы всегда сможете обратиться к этим исходникам. На картинке внизу запечатлена работа моей программки в реальном времени, там вы можете заметить и множество локальных хуков, и несколько глобальных, установленных довольно известной утилитой Punto Switcher:

Twister © 2008 (http://twister.rootkits.ru/)
Исходники к статье можно скачать по ссылке ниже.
Скачать файлы к статье «Оконные хуки: взгляд изнутри»
Twister
Установка электроплиты с доставкой. . Инверторные сплит системы установка кондиционеров воздухоочистители boneco. . Стулья кухонные продажа офисной мебели магазин диваны и кресла. . строительная компания . Tegola, Ruflex - тегола. Гибкая черепица Тегола в Москве. .
Все документы, программы, исходные тексты на этом сайте (далее - «Информация») равно как и на всех его поддоменах собраны исключительно для образовательных целей, мы не побуждаем к какому бы то ни было действию/бездействию и не отвечаем ни за какие последствия, которые имели место как следствие использования Информации. Вы используете все вышеперечисленное на свой страх и риск. В случае выявления несогласия с данным публичным договором-офертой, Вы обязаны отказаться от просмотра и копирования Информации. При возникновении спорных ситуаций Вы имеете право известить об этом администрацию сайта.
© «Rootkits», 2007. Все права защищены.