Обработчик прерывания и перехват обращения к портам i/o

Тема в разделе "WASM.WIN32", создана пользователем Wolfgang, 1 июн 2005.

  1. Wolfgang

    Wolfgang New Member

    Публикаций:
    0
    Регистрация:
    11 май 2005
    Сообщения:
    82
    Адрес:
    Russia
    Стоит передо мной такая задачка - перехватывать обращения к портам ввода/вывода. Делаю это, используя DRx. Заменяю первый элемент таблицы IDT своей ловушкой, содержащей адрес точки входа обработчика прерывания. Но из обработчика управление не возвращается! Просто жестокий молчаливый ребут... Подскажите, пожалуйста, как написать минимальный корректный обработчик или скажите, где об этом можно прочесть.
     
  2. Chingachguk

    Chingachguk New Member

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    340
  3. Wolfgang

    Wolfgang New Member

    Публикаций:
    0
    Регистрация:
    11 май 2005
    Сообщения:
    82
    Адрес:
    Russia
    код обработчика просто возврат:

    my_handler:

    iretd



    я под ХР это делаю и именно по упомянутой статье, но никак... Можно, конечно, смотреть Идой оригинальный обработчик в toskrnl.exe, но там он просто огромен. Да и, судя по статье, должно это работать
     
  4. Chingachguk

    Chingachguk New Member

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    340
    Приведи следующие данные:



    - код, реализующий вставку обработчика в idt, как ты описываешь дескриптор ловушки;

    - код, вызвавший исключение (наверное, это команда in/out ?), где он находится, его адрес (возможно, можно воткнуть отладку внутрь твоего обработчика, ибо с софтайсом исследовать не получится) ?

    - Что в стеке в момент вызова int 1 (также только debug_print)
     
  5. Wolfgang

    Wolfgang New Member

    Публикаций:
    0
    Регистрация:
    11 май 2005
    Сообщения:
    82
    Адрес:
    Russia
    А установка собственного обработчика у меня происходит так:



    это - ловушка, которую помещаю в IDT



    ttrap struc

    offs_l dw ? ; [0]

    sel dw ? ; [2]

    rsrv db ? ; [4]

    attr_ db ? ; [5]

    offs_h dw ? ; [6]

    ttrap ends



    так я ее устанавливаю:



    lea esi, new_trap ; создаем новый элемент для IDT

    assume esi:ptr ttrap

    mov eax, offset new_h

    mov [esi].offs_l, ax

    shr eax, 16

    mov [esi].offs_h, ax

    mov ax, cs

    mov [esi].sel, ax

    xor al, al

    mov [esi].rsrv, al

    mov [esi].attr_, 8Fh

    assume esi:nothing



    sidt [esp-6]

    mov esi, [esp-4] ; eax = IDT Base

    add esi, 8 ; eax -> Int1h-handler



    lea edi, orig_trap ; сохраняем оригинальный обработчик

    lodsd

    stosd

    lodsd

    stosd



    mov edi, esi ; устанавливаем новый элемент

    sub edi, 8

    lea esi, new_trap

    lodsd

    stosd

    lodsd

    stosd



    invoke DbgPrint, addr ms0



    ; Выставляем бит DE

    mov eax, cr4

    or eax, 01000b

    mov cr4, eax



    ; Port set

    xor ebx, ebx

    mov bx, Port

    mov dr0, ebx

    add ebx, 2

    mov dr1, ebx

    add ebx, 2

    mov dr2, ebx

    add ebx, 2

    mov dr3, ebx



    ; Включение ловушек GLGLGLGLGL

    ; 3333222211110000--D---EE33221100

    mov eax, 01100110011001100010011111111111b

    mov dr7, eax



    код, вызывающий исключение - out

    в стеке лежит адрес инструкции out

    (на самом деле должна, ибо с ловушками у меня пока тоже не срабатывает, срабатывает лишь на инсьрукции int 1)
     
  6. Chingachguk

    Chingachguk New Member

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    340
    Ну что я могу сказать ? ;) Очень похоже на мой код и ошибок пока вроде не нашел. Дай весь исходник, будет время - проверю под NT (я писал для 98).



    Настораживает только вот эта фраза:



    > (на самом деле должна, ибо с ловушками у меня пока тоже > не срабатывает, срабатывает лишь на инсьрукции int 1)



    Почему не работает ? Ты какие порты хочешь ловить ? Проверь те порты, воткнув команду in прямо сразу после установки прерывания. Да, и вот мой код (из тех же статей):


    Код (Text):
    1. ; подпрограмма - ловушка #1 - debug
    2. BD equ 0010000000000000b
    3. B0 equ 0001b
    4. B1 equ 0010b
    5. B2 equ 0100b
    6. B3 equ 1000b
    7. CMD_IN_AL_DX equ 0ECh
    8. ;      FEDCBA9876543210      
    9. TrapPort proc
    10.   push ebp
    11.   mov  ebp,esp
    12.   push eax
    13.   pushad
    14.   push ds
    15.   push es
    16.   pushfd
    17.   mov  ax,ss
    18.   mov  ds,ax
    19.   inc  dword ptr ds:Counter
    20.  
    21. ; Адрес возврата EIP
    22.   mov  ecx,[ebp+4]
    23. ; Адрес возврата CS->ES
    24.   mov  es,word ptr [ebp+8]
    25.  
    26. ; Причина отладочного прерывания
    27.   mov  eax,dr6
    28.   test eax,BD
    29.   jz   @@NotDebugRegs
    30.  
    31. ; Запомнить байты инструкции
    32. ; Адрес возврата EIP
    33. ; Команды мусоренья не запоминаем
    34. ;;  mov  ecx,[ebp+4]
    35. ;;  call Store_LogRecord
    36.  
    37.   inc  dword ptr ds:Counter_DRX
    38. ; Очищаем бит BD в регистре dr6
    39.   and  eax, not BD
    40.   mov  dr6,eax
    41.  
    42. ; Если это было обращение к DRx, не нужно выполнять эти команды
    43. ; Запоминаем адрес возврата и возвращаемся в наш код
    44.   add  ecx,3
    45.  
    46.   jmp  @@AfterDebugRegs
    47.  
    48. @@NotDebugRegs:
    49.  
    50. ; Запомнить байты инструкции
    51. ; Адрес возврата EIP
    52.   mov  ecx,[ebp+4]
    53. ; Запоминаем только отклики порта LPT
    54. ; Адрес возврата CS
    55.   mov  es,word ptr [ebp+8]
    56. ;  cmp  byte ptr es:[ecx-1],CMD_IN_AL_DX
    57. ;  jnz  @@SkipLogPortsCommands
    58.  
    59.   sub  ecx,1 ; in al,dx... etc (1 байт)
    60.   call Store_LogRecord
    61.   add  ecx,1 ; back to command
    62.  
    63. @@SkipLogPortsCommands:
    64.  
    65. ; Выполнить команду прямо тут ?
    66.  
    67. ; Причина отладочного прерывания в доступе к портам ?
    68.   mov  eax,dr6
    69.   test eax,B0
    70.   jz   @@NotDR0
    71. ; Очищаем бит B0 в регистре dr6
    72.   and  eax, not B0
    73. @@NotDR0:
    74.   test eax,B1
    75.   jz   @@NotDR1
    76. ; Очищаем бит B1 в регистре dr6
    77.   and  eax, not B1
    78. @@NotDR1:
    79.   test eax,B2
    80.   jz   @@NotDR2
    81. ; Очищаем бит B2 в регистре dr6
    82.   and  eax, not B2
    83. @@NotDR2:
    84.   test eax,B3
    85.   jz   @@NotDR3
    86. ; Очищаем бит B3 в регистре dr6
    87.   and  eax, not B3
    88. @@NotDR3:
    89.  
    90.   mov  dr6,eax
    91.  
    92. @@AfterDebugRegs:
    93.  
    94. ; Запоминаем адрес возврата и возвращаемся в наш код
    95.   mov  dword ptr RetAddr_UD01_EIP,ecx
    96.   mov  word ptr RetAddr_UD01_CS,es
    97.  
    98.   mov  [ebp+4],offset32 After_DebugRegisterUsed
    99.   mov  word ptr [ebp+8],cs
    100.  
    101.   popfd
    102.   pop  es
    103.   pop  ds
    104.   popad
    105.   pop  eax
    106.   pop  ebp
    107.   iretd
    108. TrapPort endp
    109. After_DebugRegisterUsed proc
    110.   pushad
    111.   pushfd
    112. ; Enable DE flag
    113.   mov  eax,cr4
    114.   or   eax,01000b
    115.   mov  cr4,eax
    116.   ;        FEDCBA9876543210FEDCBA9876543210
    117.   mov  eax,01100110011001100010011111111111b
    118.   mov  dr7,eax
    119.   popfd
    120.   popad
    121.   jmp  dword ptr RetAddr_UD01_EIP
    122. After_DebugRegisterUsed endp




    Обрати внимание вот на это:


    Код (Text):
    1. ; Если это было обращение к DRx, не нужно выполнять эти команды
    2. ; Запоминаем адрес возврата и возвращаемся в наш код
    3.   add  ecx,3




    Те (я уже всего не помню) после выполнения обращения к dr7 в коде возникает исключение - int 1 - ну у меня там борьба за перехват DRx - и адрес возврата (в стеке) указывает на ту команду, которая вызывала исключение. Возможно (пока версия) в случае int 1 происходит то же самое - ты по iretd возвращаешь код в int 1. А вот если in/out, то адрес возврата на следующую за ней команду.
     
  7. Chingachguk

    Chingachguk New Member

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    340
    Плюс еще вот что: попробуй все же воткнуть дебаг принт прямо в код - обработчик прерывания. Ну ведь айс может по прерыванию выводить что-то на экран ;) Так все станет гораздо яснее. Ну или хотя бы Beep какой-нибудь:



    ; Code that call int 1

    nop

    nop

    int 1

    nop

    nop

    ...





    my_int1_handler:

    ...

    ; assume that es:ecx=ret addr

    cmp byte ptr es:[ecx],90h ; Check for nop

    jz @@Ok_Return



    ; here we are strange state - ret_addr is ptr to ...

    ; what ?



    while ( 1 ) { Beep(); }



    @@Ok_Return:
     
  8. Wolfgang

    Wolfgang New Member

    Публикаций:
    0
    Регистрация:
    11 май 2005
    Сообщения:
    82
    Адрес:
    Russia
    Так это и есть твой код, я его за основу взял :) Сейчас заработало, хотя ничего принципиально не сменил... Шаманство какое-то :dntknw: То есть обращения к порту теперь перехватываются. Снифаю я последовательный порт. Еще есть несколько вопросов:



    1. в регистры dr0, dr1, dr2, dr3 в твоем коде вносятся значения Port, Port+2, Port+4 и Port+6. Это вроде как диапазон базовых адресов последовательного порта. А при обращении к Port+1 отладочное прерывание произойдет?



    2. почему-то по закрытии и открытии последовательного порта пользовательской программой перехват прекращается, с чем это может быть связано? Драйвер порта сбрасывает cr4?



    3. Ты в своем коде не реализовывал дальнейшую передачу управления оригинальному обработчику? А то в момент снифа портов нельзя трейситься в отладчике.



    Спасибо, что ответил
     
  9. Chingachguk

    Chingachguk New Member

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    340
    Wolfgang



    Так почему заработало-то ? ;)



    1. в регистры dr0, dr1, dr2, dr3 в твоем коде вносятся значения Port, Port+2, Port+4 и Port+6. Это вроде как диапазон базовых адресов последовательного порта. А при обращении к Port+1 отладочное прерывание произойдет?



    Ну судя по книгам (Зубков СВ, "Ассемблер - язык неограниченных возможностей", есть в инете в chm), должно быть. У меня размер диапазона - 2 байта (см. иницализацию DR7), поэтому я так перекрываю 16 байт адресов портов, по идее можно сделать 4 байта, тогда будет 32 байта. Да вроде бы работало все (+0, +1).



    2. почему-то по закрытии и открытии последовательного порта пользовательской программой перехват прекращается, с чем это может быть связано? Драйвер порта сбрасывает cr4?



    Ну обычно cr4 сбрасывает защита, для которой критичен перехват in/out ;) В тех статьях описывается именно такая, это LPT Hasp3 Key. В твоем случае (имхо) более вероятна замена системой idt (смотри за ней). Мне приходилось следить за системыми сообщениями в своем драйвере (VxD). Ну или же (опять защита) виноват сброс DR7. У меня как раз защита от этого - при сбросе DR7 я получаю руль опять по int 1. Попробуй найти место, в котором во внешнем коде уже не выполняются твои вызовы и попробуй узнать, что там с: idt, CR4, DR7(DRx).



    3. Ты в своем коде не реализовывал дальнейшую передачу управления оригинальному обработчику? А то в момент снифа портов нельзя трейситься в отладчике.



    Разумеется, отдавал - это я эмулировал hasp key так. А трейсится, конечно, нельзя - ты забираешь у айса его ресурсы. Просто возвращаешься назад - in/out уже выполнена в этот момент, если нужно, то меняешь только ответ порта (al/ax/eax).



    Подумай еще о перехвате не in/out, а более высокого уровня - абстракция HAL, это процедуры HAL.DLL - ReadPortUshort и т.п. Возможно, в твоей задаче можно обойтись без DRx, это удобнее.
     
  10. Wolfgang

    Wolfgang New Member

    Публикаций:
    0
    Регистрация:
    11 май 2005
    Сообщения:
    82
    Адрес:
    Russia
    Почему заработало? Стал восстанавливать значения отладочных регистров при выходе, только.

    Да! Самое главное - как я выяснил, мне не удается выставить значение 13-го бита dr7. Проверяю его значение сразу после установки (DbgPrint) - нет его. Такое ощущение, что система не позволяет.

    А возможна ситуация, когда система сама восстанавливает IDT????
     
  11. Chingachguk

    Chingachguk New Member

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    340
    Да! Самое главное - как я выяснил, мне не удается выставить значение 13-го бита dr7. Проверяю его значение сразу после установки (DbgPrint) - нет его. Такое ощущение, что система не позволяет.



    При инициализации обработчика или в самом обработчике ? Если в самом обработчике, то смотри мой код, а если сразу, то... воспользуйся техникой защиты - сбрось cr4 ;) Перед установкой.



    А возможна ситуация, когда система сама восстанавливает IDT????



    Ну не восстанавливает, а как бы делает новую. Я не силен в nt, но в 98-ой при загрузке процессов и тп манагер переодически меняет idt, и приходится следить за этим. Исследуй это в айсе: idt -> ...
     
  12. Wolfgang

    Wolfgang New Member

    Публикаций:
    0
    Регистрация:
    11 май 2005
    Сообщения:
    82
    Адрес:
    Russia
    Действительно! База IDT время от времени меняется, а в новой IDT в первой ячейке оригинальный дескриптор :dntknw: теперь думаю, как следить за этим программно...

    Еще один интересный момент - восстановление dr7. В книге Зубкова сказано, что при первом же срабатывании первого прерывания этот регистр сбрасывается, то есть каждый раз его приходится восстанавливать. В твоей статье сказано, что выставить значение dr7 в обработчике первого прерывания нельзя. Почему? Если выставлять значение после iretd из обработчика первого прерывания (кстати, значит, эта команда не только меняет селектор и осуществляет возврат, но и сбрасывает какой-то флаг, позволяющий выставлять dr7?), то в ХР это у меня не срабатывает. Ты не мог бы рассказать об этом подробнее?
     
  13. Wolfgang

    Wolfgang New Member

    Публикаций:
    0
    Регистрация:
    11 май 2005
    Сообщения:
    82
    Адрес:
    Russia
    Все понял! Не учитывал тот факт, что у каждого процессора свои системные регистры, в том числе отладочные и idtr. А процессора у меня два. Следовательно, и таблиц IDT (как и GDT) тоже две. Осталось только придумать способ привязывать свой поток к определенному процессору, а в обработчике прерывания определять, какой процессор выполняет поток.
     
  14. Wolfgang

    Wolfgang New Member

    Публикаций:
    0
    Регистрация:
    11 май 2005
    Сообщения:
    82
    Адрес:
    Russia
    Тут несколько сложновато написано, но смысл такой: всякий раз при выполнении такого исключения все биты в DR7 скидываются, и мы получаем в исключении #1 его "чистым". Попытка проинициализировать его внутри обработчика приведет к краху системы...





    Это - выдержка из статьи. Так вот, уважаемый Chingachguk, почему dr7 нельзя инициализировать внутри оработчика?????