Мы снова встретились? Хах, неважно.
Я как всегда, пинал сосиски. Прервав мое пинание сосисок, мне написал @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)