Обработка прерываний под Windows

Тема в разделе "WASM.WIN32", создана пользователем Zufyxe, 22 ноя 2005.

  1. Zufyxe

    Zufyxe New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2004
    Сообщения:
    137
    Адрес:
    Russia
    Проблема следующая, господа:

    Есть некий драйвер для w2k/XP. Он осуществляет обработку исключения 0d (general protection) в собственном коде. Реализована обработка путем замены соответствующего IntGate дескрипора в IDT на собственный, адресующий мой обработчик. (Почему именно так? - таковы условия задачи)

    Соответственно мой обработчик:

    1. сохраняет регистры (pushad)

    2. сохраняет сегментные регистры (es,ds,fs), инициализирует ds=ss, es=ss, fs=30h

    3. производит проверки: error code=0; вызов из ring 0;

    4. проверяет где произошло исключение

    - если проверки не проходят возвращает управление на сохраненный адрес оригинального обработчика (с восстановлением всех регистров соответственно) jmp dword ptr [xxxx]

    5. производит некоторые действия

    6. восстанавливает сегментные и прочие регистры

    7. iretd



    Вобщем, при таком раскладе, все работает нормально. Однако в процессе обработки исключения возникает необходимость обратиться к данным расположенным в PagedPool. Но для этого необходимо, чтобы в системе были разрешены перерывания (понятно почему). Однако внутри обработчика прерывания получившего управление через IntGate прерывания по определению запрещены (IF=0).

    Были предприняты следующие возможные варианты:

    1. разрешить прерывания - STI внутри обработчика

    2. использовать TrapGate и запретить CLI перед передачей управления на оригинальный обработчик.

    3. скорректировать адрес возврата iret на дополнительный код обработки, выполнить действия, передать управление ret



    Все эти действия приводят к тому, что в системе начинают, через некоторое время, падать UserMode процессы. Причем падают только в UserMode по разным адресам но все при обращении к памяти и потому, что у всех значение ds=0 и es=0.



    С моей стороны произведены следующие проверки:

    Регистры и флаги восстанавливаются перед передачей управления на 100%;

    IRQL=PASSIVE - всегда;

    вызов никаких API функций из обработчика не производится;

    после обработки исключения, управление возвращается корректно.



    Так вот: с точки зрения здоровой логики, подкрепленной intel-овской документацией операционная система не должна ничего знать о таких исключениях?

    Тогда какие будут версии происходящего? Может кто сталкивался?
     
  2. Chingachguk

    Chingachguk New Member

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    340
    Может быть, посмотреть как сделан оригинальный виндовый обработчик или там из защиты какой-нить ?
     
  3. Bitfry

    Bitfry New Member

    Публикаций:
    0
    Регистрация:
    11 авг 2004
    Сообщения:
    54
    Адрес:
    Россия, Санкт-Петербург
    Извините, что лезу в тему, про которую даже не слышал (я и про прерывания пока толком ничего не знаю :$), но меня смущает второй пункт:





    Ты уверен, что здесь всё в порядке?

    Можно взглянуть на этот отрезок кода?



    Я предполагаю, что стек должен быть кратен 32 битам.



    To All. Ещё раз извините, если в данном случае я говорю бред.
     
  4. dead_body

    dead_body wasm.ru

    Публикаций:
    0
    Регистрация:
    3 сен 2004
    Сообщения:
    603
    Адрес:
    Украина;г.Харьков;г.Н.Каховка


    через какое?





    извинием за бред.



    Zufyxe

    если хочеш, могу протестить у себя. без кода, что-то никаких соображений.
     
  5. MoonShiner

    MoonShiner New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2004
    Сообщения:
    44
    Bitfry, а ты проверь, как работает сохранение в стек сегментного регистра в 16-ти и 32-разрядном режиме=) В 32-разрядном тебе в стек пихнется дворд, младшая часть которого и есть сегментный регистр.



    dead_body, падения происходят только (по крайней мере мы наблюдали) в тех (но не во всех) потоках, которые щас в UserMode. Потоки в ядре у нас ни разу не падали. Со временем не очень понятно, но довольно быстро, т.е. если будешь работать в системе - упадет секунд за 20 что-нибудь гарантированно.



    Мы попробуем собрать драйвер для теста, но гарантии, что он будет ронять потоки также мы не дадим, поскольку сами не понимаем как это сделать=)



    ЗЫ Ответил я, поскольку вместе ковыряемся с этой проблемой...
     
  6. Godness

    Godness Мёртвый дзена

    Публикаций:
    0
    Регистрация:
    27 ноя 2002
    Сообщения:
    90
    Мы тоже сталкнулись с такой проблемой (подкачать страницу из обработчика) - решили ее путем пропачивания юзерского кода и временной передачи управления туда. Т.е. следующее



    - пачится юзерский код командой mov al, byte ptr [адресс_для_подкачки]

    - подготавливается стек для iretd на эту команду

    - устанавливаю флаг TF для юзерского кода (это проще чем мутить callgate)

    - делаю iretd и после выполнения команды mov al... автоматически получаю управление назад, т.к. сработало трассировочное прерывание int1, из которого передаю управление себе назад.

    - восстанавливаю юзерский код назад



    так реализована команда PAGEIN в айсе



    Я тоже сначала пытался подкачать страницу из обработчика непосредственно (восстанавливая флаг STI и т.д.) и вроде все работало, но система иногда случано (уже после) выдавала синих слоников с сообщением что нарушены таблицы страниц.



    MoonShiner

    можете выложить код, как вы пытаетесь подкачать страницу из обработчика? Мне интересно сравнить будет :)
     
  7. Saint German

    Saint German New Member

    Публикаций:
    0
    Регистрация:
    13 сен 2003
    Сообщения:
    222
    а меня смущает, то что ваш обработчик вызывает iret,

    т.е. вы берете на себя обработку всех исключений?

    Тогда и обрабатывайте все исключения. Выход такой -

    вешайте свой обработчик перед виндусовским и фильтруйте, то что нужно и если это не интесное вам исключение передавайте стандартному.

    Это не относится к вашему случаю, но примерно так:
    Код (Text):
    1.  
    2. __declspec(naked) NewInt3Handler()
    3. {
    4.     KIRQL   OldIrql;
    5.    
    6.     OldIrql = KeGetCurrentIrql();
    7.    
    8.     DbgPrint("Current IRQL %08X\n", (KIRQL) KeGetCurrentIrql());
    9.    
    10.     if (KeGetCurrentIrql() == PASSIVE_LEVEL)
    11.         __asm int 1;   
    12.     __asm {
    13.         pushad
    14.         int 1
    15.         popad
    16.         jmp     origInt3Handler;
    17.     }
    18. }
    19.  
     
  8. Zufyxe

    Zufyxe New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2004
    Сообщения:
    137
    Адрес:
    Russia
    Godness, проблема уже не в подкачке страниц. Разрешив прерывания (STI) в обработчике int 0d, либо осуществив возврат (IRETD) мы без проблем используем PagedPool и можем вызывать API, использующие подкачиваемые страницы. Но впоследствие, из-за этого мы получаем обнуление ds и es в юзерских тредах. А трюк с подкачкой ты придумал интересный, только int1 хватать приходится.



    Saint German, естественно, наш обработчик должен сам вызывать iret если, после фильтрации и проверок в шаге 4, он обнаруживает, что это наше исключение, нами и сгенеренное и сам его обработает. А вот не наши исключения, как я ранее писал мы сбрасываем джампом на предыдущий обработчик.



    В аттаче код, который иллюстрирует описанную ситуацию. Вкратце, что он делает:

    Патчит IDT для перехвата прерывания 0Dh. Создает системный поток, который вертится в цикле. Цикл представляет из себя вызов KeDelayExecutionThread на некоторое время и генерацию исключения:
    Код (Text):
    1.  
    2. xor eax,eax
    3. dec eax
    4. trap_code:
    5. mov edx,dword ptr [eax] - здесь
    6.  


    В обработчике мы проверяем, наше ли это исключение, т.е. произошло ли оно по адресу trap_code. Если это так, мы заносим в eax адрес валидной переменной и возвращаем управление на trap_code (iretd). Инструкция, сгенерившая исключение отрабатывает нормально, и дальше в цикле, пока мы не захотим остановить тред, поместив в переменную не 0.



    Вообще, после запуска этого драйвера, на живой w2k и на vmware с XP что-нибудь сразу валится с ds=es=0. Так что этот код воспроизводит проблему.

    [​IMG] 1298063930__MLODR.ASM
     
  9. Zufyxe

    Zufyxe New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2004
    Сообщения:
    137
    Адрес:
    Russia
    Да, и еще, достаточно в драйвере:
    Код (Text):
    1.  
    2.  cmp eax,offset trap_code
    3.  jnz go_down
    4.  sti    ; =============================                  
    5.  


    убрать STI - как все работает стабильно. Хотя, еще раз повторюсь, система, теоретически, ничего не может знать об этом исключении.
     
  10. z0mailbox

    z0mailbox z0

    Публикаций:
    0
    Регистрация:
    3 фев 2005
    Сообщения:
    635
    Адрес:
    Russia СПБ
    test byte ptr [esp+0eh],2

    jz GP_not_from_v86

    mov ebx,30h

    mov eax,32h

    mov fs,bx

    mov ds,ax

    mov es,ax



    это начало GPF_handler-а из (например) вин2003



    вы при обработке exception-а из V86 не сохраняйте и не

    восстанавливайте селекторы (они сами сохраняются и восстанавливаются) - иначе их обнулят при выходе, как и обещано в интеловской доке
     
  11. Zufyxe

    Zufyxe New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2004
    Сообщения:
    137
    Адрес:
    Russia
    z0mailbox, при добавлении в самое начало нашего обработчика строк:
    Код (Text):
    1.  
    2.  test byte ptr [esp+0eh],2
    3.  jnz jmp_prev ; переход на оригинальный обработчик
    4.  


    проблема остается. Значит дело вовсе не в v86 селекторах.
     
  12. z0mailbox

    z0mailbox z0

    Публикаций:
    0
    Регистрация:
    3 фев 2005
    Сообщения:
    635
    Адрес:
    Russia СПБ
    значит вы все-таки попадаете в этот кусок IRETD



    RETURN-TO-OUTER-PRIVILGE-LEVEL:

    [much algo skipped]

    FOR each of segment register (ES, FS, GS, and DS)

    DO;

    IF segment register points to data or non-conforming code segment

    AND CPL > segment descriptor DPL (* stored in hidden part of segment register *)

    THEN (* segment register invalid *)

    SegmentSelector = 0; (* null segment selector *)

    FI;

    OD;



    То есть вам надо на выходе вместо

    pop ds

    pop es

    pop fs

    воткнуть

    pop eax

    or ax,3

    mov ds,ax

    и.т.д.

    или еще проще

    mov ax,38h

    mov fs,ax

    mov ax,23h

    mov ds,ax

    mov es,ax

    а вот почему так, почему у вашего обработчика оказываются привилегированные селекторы данных я честно говоря с ходу не врубаюсь. может еще кто-то перехватывает GPF? может SEH-и дурят как-то хитро...
     
  13. MoonShiner

    MoonShiner New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2004
    Сообщения:
    44
    z0mailbox, test byte ptr [esp+0eh],2 идет ПЕРВОЙ инструкцией обработчика прерывания, а в jmp_prev стоит jmp Old_Handler. Т.е. мы вообще не трогаем сегментные регистры.
     
  14. z0mailbox

    z0mailbox z0

    Публикаций:
    0
    Регистрация:
    3 фев 2005
    Сообщения:
    635
    Адрес:
    Russia СПБ
    но они отчего-то же невалидные (слишком привилегированные)

    предлагаю сделать такое начало



    test byte ptr [esp+0eh],2 ; from V86?

    jnz V86_handler ; to Old_Handler

    push eax

    test byte ptr [esp+0ch],3 ; from user-mode?

    jz kernel_mode

    ; user-mode selectors check

    mov ax,ds

    and ax,3

    cmp ax,3

    je @F ; Ok

    db 0cch

    @@:

    mov ax,es

    and ax,3

    cmp ax,3

    je @F

    db 0cch

    @@:

    kernel_mode:



    и когда попадете в отладчик - все увидите сами

    и нам расскажите
     
  15. MoonShiner

    MoonShiner New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2004
    Сообщения:
    44
    Значит так. z0mailbox, большое тебе спасибо за то, что направил нас куда надо и настаивал на своем, пока мы тебя деликатно посылали=) И, вообще, всем спасибо за участие.

    На всякий случай расскажу о возникавших, после того, как поправили код, проблемах:

    1) Занесение в ds и es 23h перед iret-ом на первый взгляд работало, но только на первый. Случайно обнаружилсь проблема с Acrobat Reader, который в коде одной из своих либ имел следующее: in ax,dx. Возникало исключение C0000096h - privileged instruction. И дальше валилось в BSOD на инструкции в оригинальном обработчике pop dword ptr [0FFDFF000h]. Проблема была устранена проверкой в начале обработчика прерывания равенства нулю ds и es и установкой в них 23h. Далее следовало их сохранение и восстановление перед iret-ом. Если же какой-то из них ненулевой - сохраняли его в стек и инициализировали в 23h. Восстанавливали перед iret-ом тот, что пришел в обработчик.

    2) Изначально мы делали в обратчике следующее:
    Код (Text):
    1. push ss
    2. pop ds
    3. push ss
    4. pop es
    5.  


    Однако, при таком раскладе несколько раз выскочил синяк и иногда повисал комп на загрузке. Такой же код снял эту проблему:
    Код (Text):
    1.  push 23h
    2. pop ds
    3. push 23h
    4. pop es
    5.  
     
  16. z0mailbox

    z0mailbox z0

    Публикаций:
    0
    Регистрация:
    3 фев 2005
    Сообщения:
    635
    Адрес:
    Russia СПБ
    лишний раз доказали что в винде все на соплях висит
     
  17. Zufyxe

    Zufyxe New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2004
    Сообщения:
    137
    Адрес:
    Russia
    - может быть и так. Но все-таки, может кто-нибудь объяснить, каким образом ОС узнает о возникновении обрабатываемого нами прерывания? И почему, в конечном итоге изменяются сегментные регистры? Господа Гуру, ткните меня пожалуиста носом в Intell-овский документ, где было бы сказано, что при обработки GPF в PM сбрасываются сегментные регистры, и что программа должна их инициализировать?