Написал в драйвере загрузчик сторонних модулей: выделяем память, правим релоки, заполняем таблицу импорта и вызываем каллбэки, прокидывая запросы через основной драйвер в смапленный модуль. Выглядит примерно так: Код (C++): __try { // Вызываем каллбэк в модуле: Status = OnDeviceControl(CtlCode, Argument); } __except ( ExceptionCode = GetExceptionCode(), ExceptionPointers = GetExceptionInformation(), EXCEPTION_EXECUTE_HANDLER ) { // Передаём эксепшн в обработчик в модуле: OnException(ExceptionCode, ExceptionPointers); Status = STATUS_UNSUCCESSFUL; } И сам модуль выглядит так: Код (C++): NTSTATUS OnDeviceControl(ULONG CtlCode, PVOID Argument) { __try { *(PCHAR)(NULL) = 0; // Поднимаем исключение } __except (EXCEPTION_EXECUTE_HANDLER) { KdPrint(("Exception catched!\r\n")); } } NTSTATUS OnException(ULONG ExceptionCode, PEXCEPTION_POINTERS ExceptionPointers) { // Здесь надо передать управление на обработчик в OnDeviceControl } Есть ли какие-то простые способы передать управление на нормальный обработчик? Целевая платформа (пока) х64, патчгуард триггерить нельзя.
Проблема отошла на второй план, когда обнаружил странное поведение обработчиков в динамическом коде. Драйвер генерирует ничего не делающую функцию, поднимающую исключение: Код (C++): /* В ShellBytes такой код: sub rsp, 20h xor rax, rax mov [rax], rax // Поднимаем исключение add rsp, 20h ret */ UCHAR ShellBytes[] = { ... }; using _Shell = VOID(*)(); _Shell ShellCode = AllocExecutable(sizeof(ShellBytes)); memcpy(ShellCode, ShellBytes, sizeof(ShellBytes)); ULONG ExceptionCode = 0; PEXCEPTION_POINTERS ExceptionPointers = NULL; __try { ShellCode(); } __except ( ExceptionCode = GetExceptionCode(), ExceptionPointers = GetExceptionInformation(), EXCEPTION_EXECUTE_HANDLER ) { // Здесь повреждённый стек } Free(ShellCode); Если пошагово идти по инструкциям в отладчике, управление передаётся в обработчик и со стеком всё в порядке. Но стоит "отпустить" отладчик (F5) - в обработчик мы попадаем с повреждённым стеком. При этом, аналогичная функция внутри модуля хэндлится нормально в обоих случаях. В чём же дело?
HoShiMin, > Драйвер генерирует ничего не делающую функцию Так может вначале следует разобраться с компилером, прежде чем лезть в ядро. Если он у вас своей жизнью живёт и не контролируем, то о каком кернел коденге вообще может быть речь. Или быть может ваш подход такой - оно же собирается для км, тупо хеловорлд но собирается, а почему и я не могу" ? С таким подходом никуда не уедешь. Так и будешь ковырять крэшдампы годами свои.
А компилер здесь при чём? В юзермоде этот же шелл прекрасно хэндлится, в ядре - ломается стек в обработчике. Причём, при пошаговой отладке всё нормально, обработчик срабатывает и стек валидный. И сишный аналог, собранный, как часть драйвера: Код (C++): void Func() { *(PCHAR*)NULL = 0; } тоже хэндлится без проблем и с отладчиком, и без. Значит, у ядерного обработчика исключений есть какие-то особенности, о которых я не знаю, и которые создают разное поведение с пошаговой отладкой и без.
HoShiMin, Удивительно, вы же ведь уже давно тут и работали в км. Но я понять не могу как может быть проблема с нулевым дереференсом, обычный фаулт. Хорошо, пусть имеется проблема, но для этого есть отладчик, либо прочие средства для анализа проблемы. Так а как так получилось что спустя кучу времени и поднимая сложные весьма задачи у вас получилась засада с простым фолтом #AV и вы это не можете разобрать отладчиком. Я не понимаю.
Нет, Инди, я специально генерирую AV, чтобы проверить правильность работы хэндлеров! И в этом самая большая засада, что, отлаживая пошагово, в обработчке получаю ровный стек (делаю шаг на инструкции, генерирующей AV), но, запуская без отладки (точнее, без ПОШАГОВОЙ отладки), гарантированно получаю, как сейчас выяснилось, съехавшую ровно на 32 байта вершину, словно диспетчер забывает делать add rsp, 20h. --- Сообщение объединено, 4 ноя 2018 --- В ядре вообще можно ловить эксепшны из памяти, не принадлежащей валидному модулю? Может, я вообще пытаюсь сделать то, что нельзя? Насколько знаю, патчгуард, начиная с Win8, триггерится на ручное добавление своих обработчиков в таблицу хэндлеров - возможно, и сама обработка исключений зависит от того, где они происходят. --- Сообщение объединено, 4 ноя 2018 --- Похоже, что без отладчика эксепшн просто не обрабатывается и сразу генерит багчек. С отладчиком вилка: 1. Пошаговая отладка - правильная обработка 2. НЕ пошаговая отладка - в обработчик попадаем со сдвинутой на 32 байта вершиной Закономерный вопрос - что за чудеса?
HoShiMin, > В ядре вообще можно ловить эксепшны из памяти, не принадлежащей валидному модулю? Хороший вопрос. На старших версиях системы вероятно нет, это вероятно прибивается защитой. Даже в юм проверки на ip~image. Поставить свою ловушку в IDT не вариант, это зарубит патчгвард. Так что изначально не верный подход к задаче. Если вы шелл в ядре использовать решили, то нужно среду реализовать для его запуска, как минимум каким то образом установить ловушки и что бы на это не срабатывала защита. Все возможные пути для установки ловушек мониторит патчгвард, это IDT и KiDbgRoutine. Так что единственный вариант - его выключить или как то вмешаться в логику работы. Я бы посмотрел на его выборки, но это весьма сложная задача. > гарантированно получаю, как сейчас выяснилось, съехавшую ровно на 32 байта вершину Если бы это била защита, то вы управление вообще бы не получили в сех. Так что инфы не достаточно что бы что то предсказать, пройдите отладчиком и посмотрите что происходит.
Indy_ , Дурацкий вариант: выделить в драйвере пустую RWX-секцию достаточного размера и записывать загружаемые модули в неё, пока хватает памяти. Флажок SEC_IMAGE есть, память принадлежит валидному драйверу, эксепшны должны работать. Проверю.