Методы обнаружения трассировки

Тема в разделе "WASM.WIN32", создана пользователем Clerk, 30 авг 2008.

  1. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    sl0n
    Самотрассировка - это п.6.
    Каким либо способом поток устанавливает TF, и следит за исключением. Кривые отладчики не пропускают его, тоесть если должно развернутбся исключение отладчик фильтрует его. Тут не подходит, отслеживаются инструкции которые устанавливают TF, следующая инструкция вызывет исключение которое будет развёрнуто в потоке.
     
  2. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Снова пост потёрли, ну лан. nester7 доделаем, клинопись не знаю.)
    В общем прикинул так. Обнаружение отладчика с помощью перекрывающегося кода. Если код изменятся в текущем потоке, это ненадёжно и легко обходится, к примеру поток вызывает сервис NtWriteVirtualMemory посредством Int2E, c адресом @+2, тоесть изменяет инструкцию следующую за Int, то это не проблема обойти - установка TF перед iret в KiServiceExit и инструкция следующая за Int после исполнения вызывет отладочное исключение. Другое дело если код выполняющийся в одном потоке изменяется кодом выполняющимся в другом потоке. Тут два варианта, а именно многопроцессорная или нет система. Если система однопроцессорная, то паралельное исполнение кода в двух потоках невозможно. Но вот на многопроцессорной системе с этим возникают проблемы. Если атомарно исполняется модификация кода - это синхронизация, тут ситуация аналогично как на однопроцессорной системе. Но сервисы пишущие в память выполняют запись не атомарно.
    Как вариант - изменение аффинитета процесса, допуская для всех потоков исполнение на одном процессоре. Этим антиотладка такого типа будет сломана. В таком случае следует следить за некоторыми сервисами, к примеру NtQueryInformationProcess(ProcessAffinityMask). Видимо это наилучший способ антиотладки, единственный минус - работает только на многопроцессорных системах. В совокупности с цепочкой нетрассируемого кода(префиксы/IntXX/pop ss/[код мод. другим потоком] весьма эффективен.
     
  3. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Как я мог забыть про мои любимые отладочные регистры.
    14-й бит регистра DR6 взводится при возникновении исключения пошаговой отладки. Отладочные регистры недоступны из юзермода, но при возникновении исключения #DB в стеке фрейм(CONTEXT) содержит этот регистр с установленным 14-м битом.
     
  4. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    [BUG]
    Вначале я подумал что это тупит оля, но оказалось это очередной баг в ISR. Предыдущий баг был описан(при исполнении двухбайтной инструкции Int 3 регистр Eip в контексте(CONTEXT) фрейма(EXCEPTION_RECORD) обработчика исключений (KiUserExceptionDispatcher) указывает на второй байт инструкции).
    Юзермодный калбак KiUserExceptionDispathcer - с этой точки в юзермоде поток начинает развёртку исключений. Управление на эту точку возвращает ядро вызывая KiDispatchException(). Нормально развёртка исключения в юзермоде невозможна с взведённым TF. Баг заключается в следующем.
    При исполнении инструкции, которая вызывает исключение общей защиты с установленым TF должно развернуться исключение #GP(KiTrap0D), а исключение #DB не должно быть вызвано. При возникновении исключения #GP, в стеке сохраняется фрейм, который содержит регистр флагов, а в нём и будет взведён TF. Затем по возврату в юзермод(iretd восстановит EFLAGS, макрос EXIT_ALL) для развёртывания исключения поток начнёт исполнять код KiUserExceptionDispatcher с взведённым TF, что вызывет исключение пошаговой отладки(обработчик исключений вызывет сам себя). Нормально обработка должна произойти только исключения #GP. На самом деле первый раз будет выполнена обработка исключения #DB с адресом KiUserExceptionDispatcher, а исключение #GP не будет обработано.
    Код (Text):
    1.     push 100000000b ;Trap Flag
    2.     popfd
    3.     cli
    Заключённый в сех-фрейм этот код вызывет исключение, сех получит управление один раз при возникновении исключения пошаговой отладки после исполнения инструкции по адресу KiUserExceptionDispatcher.
    XPSP2, на остальных не проверял, но вроде тоже самое.
     
  5. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Код который фиксит этот баг(немного проблемно скомпилить):
    Код (Text):
    1. ExceptionHandler proc C
    2.     mov edx,dword ptr [esp + 4]
    3.     assume edx:PEXCEPTION_RECORD
    4.     cmp [edx].ExceptionCode,STATUS_SINGLE_STEP
    5.     jne return_
    6.     mov eax,dword ptr [KiUserExceptionDispatcher + 2]
    7.     mov eax,dword ptr [eax]
    8.     cmp [edx].ExceptionAddress,eax
    9.     jb return_
    10.     add eax,15  ;Maximum instruction size
    11.     cmp [edx].ExceptionAddress,eax
    12.     ja return_
    13.     mov edx,dword ptr [esp + 3*4]
    14.     assume edx:PCONTEXT
    15.     and [edx].regEFlags,Not EFLAGS_TF   ;On any event
    16.     and [edx].regDr6,Not HB_EVENT_TRAP  ;On any event
    17.     push FALSE
    18.     push edx
    19.     Call ZwContinue
    20. return_:
    21. ;   [...]   Normal SEH handler
     
  6. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Тут пришёл к выводу что отношение времени исполнения Int2A к времени исполнения тестового кода(если он не трассируется) является константой для данного процессора и не зависит от его частоты и произ. системы. Если код трассируется, то эта константа уменьшается. Обойти это из юзермода - никак, из кернелмода - ставить задержку в ISR. Плохо представляю как это будет работать на других процессорах, но могу предположить что константа не сильно изменится, так как число тактов для инструкций не сильно изменили а код одинаков.
    Код (Text):
    1. SharedUserData  equ 7FFE0000h
    2.  
    3. ;Определяет число прерываний 0x2A вызванных в течении 20 миллисекунд.
    4. QueryInterruptPerformance proc uses ebx edi
    5.     mov edi,SharedUserData
    6.     xor ebx,ebx ;Счётчик числа прерываний.
    7.     assume edi:PKUSER_SHARED_DATA
    8.     mov ecx,[edi].TickCountLow
    9. ;Ожидание следующего тика чтобы начать счёт с начала тика.
    10. wait_next_tick_:
    11.     cmp ecx,[edi].TickCountLow
    12.     je wait_next_tick_
    13.     add ecx,DURATION
    14. interrupt_:
    15.     Int 2Ah
    16.     inc ebx
    17.     cmp [edi].TickCountLow,ecx
    18.     jb interrupt_
    19.     mov eax,ebx
    20.     ret
    21. QueryInterruptPerformance endp
    22.  
    23. QueryStrayPerformance proc uses ebx edi
    24.     mov edi,SharedUserData
    25.     xor ebx,ebx ;Счётчик числа прерываний.
    26.     assume edi:PKUSER_SHARED_DATA
    27.     mov ecx,[edi].TickCountLow
    28. wait_next_tick_:
    29.     cmp ecx,[edi].TickCountLow
    30.     je wait_next_tick_
    31.     add ecx,DURATION
    32. interrupt_:
    33.     inc ebx
    34.     cmp [edi].TickCountLow,ecx
    35.     jb interrupt_
    36.     mov eax,ebx
    37.     ret
    38. QueryStrayPerformance endp
    39.  
    40. comment '
    41. N = QueryInterruptPerformance()
    42. M = QueryStrayPerformance()
    43.  
    44. dT(Int 2Ah) / dT(StrayTime) = Const
    45. dT(Int 2Ah) / dT(TestRoutine) = Const
    46.  
    47. Ti = dT(Int 2Ah)
    48. Ts = dT(inc ebx   +   cmp [edi].TickCountLow,ecx   +   jb interrupt_)
    49.  
    50. Ti / Ts = Const, Ti >> Ts, M >> N
    51.  
    52. (Ti + Ts) * N = DURATION -> Ti = DURATION / N - Ts
    53. Ts * M = DURATION -> Ts = DURATION / M
    54. (Ti / Ts) = (DURATION / N - Ts) / (DURATION / M) = M / N - 1'
    55.  
    56. ;Разделяемую память не следует юзать, её адрес в версиях меняется, к томуже 0x2A прерывание возвращает число тиков в Edx:Eax. Нужно поставить Int 2Ah вместо обращений к TickCountLow и пересчитать отношение (Ti / Ts).
    57. ;Сдесь используется чтобы не усложнять.
    58.  
    59. QueryKernelUserPerformanceRelation proc uses ebx
    60.     Call QueryInterruptPerformance
    61.     mov ebx,eax
    62.     Call QueryStrayPerformance
    63.     inc ebx ;На случай защита от исключения при делении на ноль.
    64.     xor edx,edx
    65.     div ebx
    66.     dec eax ;(Ti / Ts) = Const
    67.     ret
    68. QueryKernelUserPerformanceRelation endp
    69.  
    70. comment '
    71. На P4(2 x 3014MHz) - результат колеблется в небольших пределах возле ~340, аффинитет не влияет на него, приоритет потока аномально смещает немного результат, причём занижая отношение независимо от того повышается или понижается приоритет, хз почему.
    72. Интересно на варе потестить.'
    хм.
     
  7. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    Clerk
    Код (Text):
    1. ...
    2. mov ecx,[edi].TickCountLow  ;; допустим в ECX попало что-то
    3.                 ;; больше чем 0xFFFF'FFFF минус DURATION,
    4.                 ;; но меньне чем 0xFFFF'FFFF
    5. ...
    6. add ecx,DURATION        ;; тут перешагнули через ноль
    7. ...
    8. cmp [edi].TickCountLow,ecx  ;; допустим [edi].TickCountLow еще
    9.                 ;;  не перешагнул через ноль
    10. jb interrupt_           ;; куда попадем?
    11. ...
     
  8. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Clerk
    Ага, то говоришь, что замеры времени это ненадежно и бесполезно, то сам начинаешь изобретать "хлипкие" велосипеды ;)
     
  9. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    q_q
    Само собой разумеется что проверки перехода через ноль нужны, как и значения в регистре Edx возвр. Int 2A.
    Сдесь это не важно, переход через ноль произойдёт примерно один раз в 50 суток. И какова вероятность что код войдёт в этот цикл ожидания ?
    leo
    Трассировщик должен быть мощным, нужно знать с чем бороться. Хлипкие - это не тут, а в гугле.
    В реальном софте подобное не применяется, все юзают FindWindow(), тск и тому подобный гуан.
     
  10. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    q_q
    offtop.
    Ты пишешь код. Скажи, хотябы раз ты выполнял проверку выравнивания стека на границу в 4-е байта ?, а вероятность ошибки огромная - запру я в стек 1 байт - и весь код падёт на первом - втором сервисе. Это искать баги в примерах описывающих технику ыы.
     
  11. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    leo
    Апстену.
    Код (Text):
    1. DURATION    equ 20  ;Milliseconds
    2.  
    3. QueryInterruptPerformance proc uses ebx esi
    4.     xor ebx,ebx
    5.     mov esi,DURATION
    6.     Int 2Ah
    7.     mov ecx,eax
    8. wait_:
    9.     Int 2Ah
    10.     cmp eax,ecx
    11.     je wait_
    12. tick_:
    13.     mov ecx,eax
    14. calc_:
    15. ;   Int 2Ah
    16.     Int 2Ah
    17.     Int 2Ah
    18.     inc ebx
    19.     cmp eax,ecx
    20.     je calc_
    21.     dec esi
    22.     jnz tick_
    23.     mov eax,ebx
    24.     ret
    25. QueryInterruptPerformance endp
    26.  
    27. QueryStrayPerformance proc uses ebx esi
    28.     xor ebx,ebx
    29.     mov esi,DURATION
    30.     Int 2Ah
    31.     mov ecx,eax
    32. wait_:
    33.     Int 2Ah
    34.     cmp eax,ecx
    35.     je wait_
    36. tick_:
    37.     mov ecx,eax
    38. calc_:
    39.     Int 2Ah
    40.     inc ebx
    41.     cmp eax,ecx
    42.     je calc_
    43.     dec esi
    44.     jnz tick_
    45.     mov eax,ebx
    46.     ret
    47. QueryStrayPerformance endp
    N = QueryInterruptPerformance()
    M = QueryStrayPerformance()
    Kcpu = M / N - 1
    Раскомент Int2A -> Kcpu + 1.
    XPSP2/P4 - 2x3014: погрешность 5%.
    Кто способен привести ещё более эффективный метод для определения временных задержек ?
     
  12. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Тестовый пример.
     
  13. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Потестите.
     
  14. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    [offtop]
    Clerk
    искать баги в примерах описывающих технику ыы
    Прошу прощения, у меня сложилось впечатление, что ты запостил готовое решение.
    [/offtop]
     
  15. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Clerk
    Раз ты вычисляешь Kcpu*1000, то в QueryKernelUserPerformanceRelation нужно заменить dec eax на sub eax,1000
     
  16. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    leo
    Ну да точно, описалсо, но это не важно. Что никто не может потестить ?
    Плиз тока не на варе.
     
  17. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Да и вообще после ваших усталих коментов нихуя больше постить не хочу Ж
     
  18. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Clerk
    Не кипятись :)
    Проверил на P4 Northwood 3.2Ггц, P4 Prescott 3.0Ггц, PentiumD 3.0Ггц, Athlon 64 3200+ (2Ггц)
    На всех XP SP2. При однократных запусках, как и ожидалось, все путем - на всех процах без трассировки (за вычетом 1000) получается ~965-1000 (на P4 c HT иногда и за 1000 зашкаливает), а с трассировкой ~13-18 без HT и до 50 с вкл. HT

    Но ес-но можно смоделировать и сбойные ситуации - например, запускаем сразу несколько копий, затем аккуратно выстраиваем окошки в ряд и начинаем подряд нажимать OK - в итоге при трассировочном проходе могут получаться цифры > 9000. Поэтому ес-но нужен доп.контроль сбоев
     
  19. _basmp_

    _basmp_ New Member

    Публикаций:
    0
    Регистрация:
    10 июл 2005
    Сообщения:
    2.939
    атлон-м ХР 2000+ xpsp2. ~1.67Ггц. 1) ~1990 2) ~1050
     
  20. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Просто вывод кривой, надо было обнулить буфер перед вторым выводом строки.
    Отнимать от Kcpu 10^3 не следует, иногда отрицательные значения появляются.
    При аффинитете 11B второе значение приближается к первому иногда, но при аффинитете 1 - всегда на половину меньше(для многих одновр. зап. копий).