четверг, 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), что не может меня не радов...