Мое почтение всем. Ради интереса и практики перехватываю KiTrap0E в IDT. Через некоторое время (а иногда сразу) получаю различные BSoD'ы. Т.е. что-то не учел. Сначала я сохраняю используемые регистры, затем копирую eip, cs и eflags в глобальный массив и подменяю cs:eip на адрес в своем коде. Затем вызываю оригинальный обработчик, и когда он выполняет iretd, снова получаю управление. Возвращаю оригинальные eip, cs и eflags в стек и делаю iretd. Код (Text): __declspec(naked) VOID HookFunc() { __asm { ;KTRAP_FRAME еще не сохранен, регистры менять нельзя. push ecx push ds mov cx, 0x23 mov ds, cx ;теперь можно обращаться к данным через ds. mov ecx, dword ptr [esp] mov word ptr [DsArea], cx mov ecx, [esp + 4] mov dword ptr [EcxArea], ecx add esp, 0x8 mov cx, es mov word ptr [EsArea], cx mov cx, 0x23 mov es, cx mov dword ptr [EsiArea], esi mov dword ptr [EdiArea], edi ;Регистры сохранены, копируем оригинальные eip, cs и eflags. cld lea esi, dword ptr [esp + 4] lea edi, dword ptr [IretAddr] mov ecx, 3 rep movsd mov ecx, label mov dword ptr [esp + 4], ecx mov dword ptr [esp + 8], 8 ;Восстанавливаем регистры. push dword ptr [EcxArea] mov esi, dword ptr [EsiArea] mov edi, dword ptr [EdiArea] mov cx, [EsArea] mov es, cx mov cx, [DsArea] mov ds, cx pop ecx jmp [PrevKiTrap0E] label: ;KTRAP_FRAME восстановлен, регистры менять нельзя. Сохраняем регистры. push ecx push ds mov cx, 0x23 mov ds, cx mov ecx, dword ptr [esp] mov word ptr [DsArea], cx mov ecx, [esp + 4] mov dword ptr [EcxArea], ecx add esp, 0x8 mov cx, es mov word ptr [EsArea], cx mov cx, 0x23 mov es, cx mov dword ptr [EsiArea], esi mov dword ptr [EdiArea], edi ;Выделяем место в стеке, копируем оригинальные eip, cs и eflags. cld sub esp, 0xC mov edi, esp lea esi, [IretAddr] mov ecx, 3 rep movsd ;Восстанавливаем регистры. push dword ptr [EcxArea] mov esi, dword ptr [EsiArea] mov edi, dword ptr [EdiArea] mov cx, [EsArea] mov es, cx mov cx, [DsArea] mov ds, cx pop ecx ;Jump home. iretd } } Что я не учел в своем коде? Проверял в отладчике, значение регистров на входе и выходе в обработчик не изменяются. Однако все равно валится, помогите найти проблему. Заранее благодарен. P.S. Я знаю про более простые способы перехвата, но хочется понять, в чем ошибся.
Система многопроцессорная/многоядерная? Какие бсоды? Хотя бы один пример с выводом !analyze -v И вообще, код по вызову оригинального обработчика очень кривой.
Great Два ядра (дело происходит в VM). !analyze -v предоставлю чуть позже. А что именно криво в коде? ADD: -- А, подозреваю, тебе не понравились манипуляции в стеке в коде сохранения регистров. Проблема в том, что я не могу обращаться к данным через ds/es. Можно переделать на обычные mov'ы относительно ss, но как-то я об этом не подумал, когда писал.
Лень разбираться, но предположу что код ошибки не удаляется со дна стека. Если бы сразу вызывался системный ISR, то всё былобы тру, иначе в стеке лишний дворд. В любом случае необходимо сформировать трап-фрейм, иначе размаскировать прерывания вы не можите.
Clerk Я бы увидел в отладчике лишний dword. В любом случае, код ошибки выталкивает ориганальный обработчик -- KiTrap0E. А зачем KTRAP_FRAME? Моя iretd восстановит значение eflags, в котором IF будет установлен. Итак, при запуске моего драйвера происходит исключение в lsass.exe: Кроме того, создать свой KTRAP_FRAME и вызвать KiTrap0E не получится, т.к. стек должен быть чист на момент выполнения KiTrap0E. Сама KiTrap0E делает нечто в этом роде: if (esp - fs:[_NT_TIB.StackBase] + 0x8C != 0) Создать два KTRAP_FRAME, один из которых указывает на KiServiceExit2. А второй KTRAP_FRAME перетрет сохраненный мной контекст. В итоге, я решил сохранить cs:eip & eflags в глобальной переменной, чтобы не держать их в стеке и подменять адрес возврата, созданный процессором на свой.
Mika0x65 Ваш код в общем верный, проверил каждую инструкцию. Но проблема в флажках. Вы не маскируете IF. Тоесть ваш Post' хэндлер получит управление с IF, а должен с !IF. Если вы замаскируете IF, то ISR #PF приведёт к багчеку, так как проверяет IF.
Так, давай по шагам. К примеру возьмем переход из user mode. 1. IF установлен, прерывания разрешены. 2. Происходит #PF. Процессор сохраняет в стеке ядра eflags, cs, eip и сбрасывает IF. Прерывания запрещены. 3. Вызвается мой обработчик. Он меняет eflags, но не трогает IF. Прерывания запрещены. 4. Вызвается код KiTrap0E. что там с прерываниями происходит я не знаю. 5. KiTrap0E выполняет iretd. IF восстанавливается, прерывания разрешены. 6. Выполняется мой код. Прерывания разрешены. 7. Мой код выполняет iretd повторно восстанавливая eflags. Прерывания разрешены. Когда и где надо сбросить/установить IF? И надо ли вообще? Кроме того, ошибка происходит в user mode, предположительно, в ntdll. Судя по всему, я все же что-то испортил в контексте, но не пойму что.
Говорил же что нельзя размаскировать прерывания без формирования трап фрейма. Да и вобще возможна рассинхронизация, пока первый процессор будет хэндлить фолт, возникающий на другом затрёт сохранённые в переменных регистры. И загружать ядерный селектор нельзя, этим вы меняете прев. мод. Вобще так не делается, всё наперекосяк.
Подумав я совсем не вижу решения, разве что скопировать себя весь код ISR с нулевым NL(без раскрытия процедур). Это перестройка графа, всё тотже морфинг(его частный случай).
Mika0x65 Да, я тоже сразу заметил, что KiTrap0E восстановит IF в еденицу и кранты вам) первое же прерывание таймера (да и вообще любое) все угробит, т.к. нет сформированного треп-фрейма
Great Clerk Все равно не улавливаю. Допустим, выполняется мой код (который после вызова оригинального обработчика), прерывания разрешены. Происходит прерывание, например, того же таймера. Обработчик прерывания таймера сохранит контекст в свой KTRAP_FRAME и восстановит контекст по выходу. Почему все должно угробиться? В принципе, как вариант, я могу изменить eflags, сохраненный процессором в стеке -- сбросить IF. Все равно у меня есть копия eflags в глобальной переменной, откуда eflags можно восстановить, выполняя мою iretd. Сегодня попробую так сделать.
Mika0x65 Для всех прерываний стек общий и загружается он при вызове шлюза(смене кпл на нулевой). Тоесть предыдущий стек будет затёрт и после возврата управление будет передано вникуда. Тут наверно иные проблемы могут быть, так как стек не переключается. Задача не сформирована, всякая попытка шедулера и прочего механизма разрулить поток приведёт к краху. Это нарушение общей архитектуры NT.
О, это интересно. Получается, что IF должен быть сброшен все время, пока работает обработчик прерывания. Тогда попробую сбросить IF в eflags, который в стеке. А когда сам буду делать iretd, верну eflags на место.
Mika0x65 Если вы сбросите IF, то менеджер памяти будет считать что фолт произошёл на высочайшем IRQL, это незамедлительно приведёт к багчеку с кодом IRQL_NOT_LESS_OR_EQUAL.
Получается, без серьезных модификаций это сделать невозможно. В принципе, можно поколдовать с PIC'ами, чтобы запретить прерывания не с помощью IF, но пока, наверное, оставлю. Спасибо за разъяснения.
> Если вы сбросите IF, то менеджер памяти будет считать что фолт произошёл на высочайшем IRQL, это незамедлительно приведёт к багчеку с кодом IRQL_NOT_LESS_OR_EQUAL. дык откуда он узнает об этом? если if сброшен-то.
n0name Насколько я понимаю, обработчик смотрит в стек. На всякий случай решил проверить. Добавил and 'dword ptr [esp + 8], 0xFFFFFDFF' перед 'cld'. Получил: На этом участке кода: Код (Text): f7ba9652 668b0dae9fbaf7 mov cx,word ptr [rk!DsArea (f7ba9fae)] f7ba9659 668ed9 mov ds,cx f7ba965c 59 pop ecx f7ba965d ff25b09fbaf7 jmp dword ptr [rk!PrevKiTrap0E (f7ba9fb0)] f7ba9663 51 push ecx ;<---------Exception! f7ba9664 1e push ds f7ba9665 66b92300 mov cx,23h f7ba9669 668ed9 mov ds,cx Во-первых, получается, что обработчик все же выполнился без BSoD'а, во-вторых -- откуда там взяться исключению? Значение esp весьма легально: esp=eebc0dd8 eebc0dd8: 0081fd70 00000023 00000000 00000000 В стеке лежат ss:esp из user mode. Что происходит?
n0name Считает со стека: Код (Text): _KiTrap0E proc ENTER_TRAP kite_a, kite_t if FAST_BOP cmp dword ptr PCR[PcVdmAlert], 0 jne Kt0eVdmAlert endif MODIFY_BASE_TRAP_FRAME mov edi,cr2 ; ; Now that everything is in a sane state check for rest of the Pentium ; Processor bug work around for illegal operands ; cmp _KiI386PentiumLockErrataPresent, 0 jne PentiumTest ; Check for special problems NoPentiumFix: ; No. Skip it sti test [ebp]+TsEFlags, EFLAGS_INTERRUPT_MASK ; faulted with jz Kt0e12b ; interrupts disabled?