четверг, 2 декабря 2021 г.

Как мы на Kaspersky в KSE охотились

 

Где-то примерно неделю назад один мой коллега приметил одну замечательную вещь: Kaspersky интересным образом протянул свои ручки к Kernel Shim Engine.

В итоге мы собрались где-то вчера разгребать и смотреть, что он там забыл. Также попытались распилить и отловить шим Касперского, но об этом чуть позже.

Вводные

Начнем с понимания, что это вообще за зверь такой - Kernel Shim Engine.
Технически Kernel Shim Engine (далее KSE) предоставляет "обертки" для драйверов устройств, а также обеспечивает дополнительную поддержку и обработку ошибок драйверов устройств. У него есть своя специальная база данных, мы затронем её позже.

Также стоит поговорить о кое чем еще.

Инициализация шимов

Если говорить в кратце и не вдаваться в подробности работы, то шимы инициализируются на этапе загрузки системы, где-то сразу после того, как инциализировался HAL (Hardware Abstraction Layer) и WMI (Windows Management Instrumentation) с ETW (Event Tracing for Windows). После прогрузки HAL, WMI и ETW вызывается функция KseInitialize(), а после нее KseRegisterShim(). Эти функции официально не документированы, но находятся в некоторых заголовочных файлах WDK.

Инициализация "Built-in" шимов драйверов

В конце инициализации HAL происходит инициализация шима DriverScope, этот шим преоставляется по стандарту системой:

Затем, во время инициализации WMI/ETW также инициализируется функция KseVersionLieInitialize(). Данная функция инициализирует шимы KmWin7VersionLie, KmWin8VersionLie и KmWin81VersionLie (хоть тесты и производились на десятке, но подобный шим с версией "10" я не обнаружил):


Потом вызывается функция KseSkipDriverUnloadInitialize(), которая инициализирует шим SkipDriverUnload.

Функции Касперского в ядре

Окей, вроде +- разобрались. Теперь, какие функции были найдены мной, затрагивающие шимы:

Доступ был только к функции к KseKasperskyInitialize():

Сперва мы решили посмотреть, как поведет себя Касперский при манипуляции с реестром, а конкретнее:

  • HKEY_LOCAL_MACHINE\System\CurrentControlSet\Policies\Microsoft\Compatibility
    С ключами DisableDeviceFlags и DisableDriverShims (REG_DWORD), их значения равны единицы, очевидно по названиям ключей, что они делают при значении 0x01.
  • HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Compatibility
    С ключем DisableFlags (REG_DWORD) со значением 0x01 (отключает функциональность драйвера).

В нашем случае Касперский никак себя не повел, тем более он спокойно дал изменить значения регистров, т.е эти ключи реестра не мониторятся Касперским.

Как поломать KSE

Можно сломать KSE двумя путями: мониторинг драйвера через Verifier и с помощью удаления drvmain.sdb.
Мы вполне имеем право поставить Verifier на драйвер Касперского, ну или удалить сам drvmain.sdb, но скорее всего это вызовет BSOD.

drvmain.sdb

Мы все же решили для начала порыться в drvmain.sdb. Стоит сказать, что до мониторинга и удаления мы не дошли, а почему - вы поймете позже.

drvmain.sdb - стандартная база данных "легальных" драйверов и не только. Помимо драйверов, там также находится информация о шимах и других вещах.
Этот файл находится в "%systemroot%/apppatch/". Файлы с расширением sdb можно открыть, к примеру, через SDBExplorer.

Видим в drvmain вот такую картину:


Да, список драйверов Касперского, ничего удивительного. Можно было бы ставить Verifier на klhk.sys и идти отлавливать шим Касперского, но не все так просто.

KSHIM и почему все печально закончилось

В drvmain, как и упоминалось выше, есть не только перечисление драйверов, а также некоторые шимы.
Представляются они в виде структуры KSHIM, которая по сути является структурой KSE_SHIM, только слегка измененной:

typedef struct _KSE_SHIM {
  _In_ ULONG Size;
  _In_ PGUID ShimGuid;
  _In_ PWCHAR ShimName;
  _Out_ PVOID KseCallbackRoutines;
  _Inopt_ PVOID ShimmedDriverTargetedNotification;
  _Inopt_ PVOID ShimmedDriverUntargetedNotification;
  _In_ PVOID HookCollectionsArray;
} KSE_SHIM, *PKSE_SHIM;

Вот как выглядит KSHIM Касперского:

Заметьте, что поле MODULE несет значение "NT kernel component". А теперь можем посмотреть на ndis шимы:

 

Да, тут поле MODULE со значением "ndis". Думаю, можно догадаться, в чем проблема.

Ага, это значит, что шим Касперского существует в пространстве самого ядра системы. А это значит, что кроме как накидывая Verifier на ядро мы отследить шим Касперского не сможем. Вот, только если накидывать его на ядро, сломается не только Касперский, но и сама система. Прикольно, правда?

Возможный путь отлова шима Касперского

В мою голову лезет только анализ дампа системы. Т.е мы должны завершить систему аварийно, при этом сгенерировав дамп системы, где был запущен Касперский. Команда ".crash" (если не ошибаюсь) из windbg вполне решит эту проблему. Я, правда, не уверен, что мы сможем вытащить что-то дельное, но попытка не пытка, как говорится.


Послесловие и догадки

Теоретически, _KSE_SHIM - пользовательский ввод. Есть смысл посидеть над драйвером ndis.sys (сама структура оттуда). Однако, стучать туда через непривелегированного пользователя мы не сможем, но вполне вероятно, что до туда сможет достать TrustedInstaller.

четверг, 5 августа 2021 г.

Путешествие по недрам системы: анализ дампа, недокументированные NT-функции и причем здесь GDI

 

Мы снова встретились? Хах, неважно.

Я как всегда, пинал сосиски. Прервав мое пинание сосисок, мне написал @vcprocles с вот таким вопросом:

 
 

Вот так, это послужило мотивацией для взятия образа дампа (за что спасибо) и дальнейшего написания этой статьи, хоть и сумбурной слегка. Фактически, эта статья - ответ на вопрос от Procles.

Так как core analyzer решил не жрать дамп, я воспользовался windbg, благо он позволяет пожирать безразмерные дампы (мне дали с размером ~1.5 gb). Можно и побаловаться, открываем windbg и анализируем дамп:


Можно заметить, что у нас не прогрузился PEB, но это не важно. Нас интересуют вот эти 2 строки:

Arg2: 0xfffff8011adfdff2, Address of the instruction which caused the bugcheck
Arg3: 0xffffd601e1ff2920, Address of the context record for the exception that caused the bugcheck

В этом сегменте произошел сбой, предварительно вызвав "bugcheck":

0xc0000005 - "Нарушение прав доступа"

Теперь более-менее понятно, что произошло. Скорее всего, какой-то указатель сослался на неинициализированный сегмент памяти или банально переполнился стек. Мне стало интересно, что творилось в регистрах и что смогло вызвать сбой, поэтому я решил поглазеть на это все:



Если честно, то все по стандарту, вызвался XRSTORS, восстановив состояние x87 из XSAVE и вызвался SwapContext для сохранения вообще всего в _KTHREAD, если не ошибаюсь.

Гораздо полезнее будет посмотреть на поток в момент сбоя:

Вызвались дебаг-функции перед сбоем. Чудно. Также мы видим причину этого сбоя, некий NtUserReleaseDС убил нам систему. В общем, отсюда мы также можем понять, что именно произошло:

Да, проблема с указателями и стеком.. В ядре..

Мне также захотелось заглянуть в черный ящик. BSD и NTFS мне ничего не дали, но PNP выдал вот это:

Не могу сказать, что подключенный носитель мог вызвать сбой, хотя могла произойти какая-то вещь, связанная с носителем, что повлекло за собой BSOD. Procles предположил, что причиной мог стать андервольтинг (т.к вызвался процесс ThrottleStop.exe).

Нырнем!


Немного глубже

Мы уже посмотрели, что происходило в потоке и выяснили, что повлияло на BSOD и процесс, вызвавший это, но есть несколько более "глубоких" вещей, которые стоит рассмотреть.

Все же мы посмотрим еще раз на поток, глубже, чем ранее:

Мне пригодится адресное пространство на момент сбоя, я выделил его. Мне было интересно, что происходило не только с функциями, но и с драйверами. К сожалению или к счастью, драйвера вообще не упоминались в сегменте, но обнаружилось несколько интересных вещей:

Хардварная функция с 1 битом


 Подозрительное прерывание

 

Тоже прерывание. Об этой функции интернетам ничего не известно


Все это принадлежит HAL (Hardware Abstraction Layer), что возможно имеет отношение к подключенному носителю во время сбоя, тот самый, что выдал мне PNP.

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

Если честно, то я немного в тупике. Я проанализировал также процесс (ThrottleStop.exe) на наличие других аномальных вещей, но особо ничего не нашел. Возможно, на вопросы ответит NtUserReleaseDС, стоит проверить.


Недокументированные NT-функции. Ресёрч

Стоит сказать, что у Винды есть два основных API: WinApi (aka Win32) и NTAPI. Если первый документирован и функции предельно понятны, то второй таковым не является, он почти весь является недокументированным и в некоторой степени устаревшим, эдакий атавизм, да. Но все же есть несколько сайтов, предоставляющие нам немногочисленную документацию по некоторым функциям.

NtUserReleaseDС как раз пришел к нам из NTAPI и входит в разряд недокументированных, поэтому будем рыться в чем есть.

Я первым делом сразу полез в исходники ReactOS, чтоб посмотреть, что данная функция делает, но забавно, что от нее избавились еще в далеком 2007:

https://reactos.org/pipermail/ros-diffs/2007-August/018150.html

Да, ничего нам оно не даст. Но я обнаружил интересную ссылку, где содержалась искомая функция: https://systemroot.gitee.io/pages/apiexplorer/d7/d9/usercli_8h-source.html. Ииии, да.. Это обертка другой функции:

#define NtUserReleaseDC(hwnd,hdc)  NtUserCallOneParam((ULONG_PTR)(hdc), SFI__RELEASEDC)

Что же, придется что-то делать. Благо, функция была в ReactOS и тут мы уже можем от чего-либо отталкиваться: https://doxygen.reactos.org/d6/dee/simplecall_8c_source.html (строка 145). Стоит сказать, что там есть парочка неаккуратных мест, где возможна ошибка, по типу:

psmwp->acvr = ExAllocatePoolWithTag(PagedPool, count * sizeof(CVR), USERTAG_SWP);

Или:

Result = (DWORD_PTR)DesktopHeapAddressToUser((PVOID)Param);

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


Причем здесь GDI?

GDI - это компонент, составляющий пользовательский интерфейс. Он не отвечает за построение окон или меню-баров. Его дело - отображение шрифтов, работа с палитрами и прочим.

Так причем GDI? Дело в том, что при поиске ответа что из себя представляет NtUserReleaseDС я все время косвенно сталкивался с топиками по GDI. И знаете... Все оказалось намного проще когда я убрал приставку "Nt":



Да, то, что мы искали - нативная функция, судя по всему зарезервированная Виндой. В это же время она имеет свой прототип в WinApi. Как-то так.


Так какая истинная причина ошибки?

Не был освобожден "контекст устройства" (DC) для передачи следующему приложению, что повлекло за собой неожиданные последствия.


Вот такие пирожки с котятами. Будьте здоровы и не стреляйте себе в колена указателями.


~slowdeath.

==========

Вы всегда можете купить мне сигареты:

4890 4947 2029 0345 (qiwi)

I was visited by death again: UEFI руткиты в схемах и мемах, поговорим о заражении

  Спустя, наверное, продолжительное время, я потихоньку собираюсь с силами (мне просто надоело залипать в Krita), что не может меня не радов...