#GP: Причина краха.

Тема в разделе "WASM.NT.KERNEL", создана пользователем Clerk, 18 май 2009.

  1. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Всем привет. Такая проблема.
    Захват Sysenter. В IA32_SYSENTER_CS загружается селектор, индексирующий ядерный дескриптор с базой, пересчитанной как (DispatcherEntry[в неподкачиваемом пуле] - IA32_SYSENTER_EIP[KiFastCallEntry]), гранулярность - 1.
    Рядом в GDT создаются есчо три дескриптора(требует Sysenter/Sysexit), второй для ядерного сегмента стека, третий и четвёртый соответственно юзермодные сегменты кода и стека. Три последних сегмента имеют нулевые базы, дескрипторы аналогичные как по умолчанию:
    Код (Text):
    1. GDT;
    2. ;Sysenter CS
    3. +120        3AC9FFFF    ;Base: 0x1293AC9(+KiFastCallEntry:VALID)
    4. +124        014F9B29
    5. ;Sysenter Ss
    6. +128        0000FFFF    ;Base: 0
    7. +12C        00CF9300
    8. ;Sysexit Cs
    9. +130        0000FFFF    ;Base: 0
    10. +134        00CFFB00
    11. ;Sysexit Cs
    12. +138        0000FFFF    ;Base: 0
    13. +13C        00CFF300
    В мср:
    IA32_SYSENTER_EIP: ntoskrnl!KiFastCallEntry
    IA32_SYSENTER_CS: 0x120
    В IA32_SYSENTER_ESP дефолтный указатель на DPC-стек.
    P4, второе ядро отключено(в бутини). База не менялась:
    Код (Text):
    1. GDTR:
    2.    Base     8003F000
    3.    Limit        000003FF
    Собственно код: ftp://files.virustech.org/Code/Sysenter/CsBase/
    Рушится на #GP(KiTrap0D). Стек:
    Код (Text):
    1. ErrorCode   00000000
    2. Eip     804DE36F    ;Kei386EoiHelper -> IRetd
    3. Cs      00000008    ;KGDT_R0_CODE
    4. EFlags      00010082
    5. Esp     8057D695    ;ntoskrnl!CcMapData + 0xCB
    6. Ss      00000120
    Исключение возникает в Kei386EoiHelper() на инструкции IRetd, видимо при обработке аппаратного прерывания, при возврате на прерванный код. Не валидный указатель на стек, указывает внутрь ядра(не доступна для записи страница), селектор сегмента стека изменённый 0x120(база нулевая, лимит -1).
    Не могу понять почему рушится и откуда изменяется указатель на стек, сутки ковырял и ничего.
     
  2. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Хотя нет, прерывание ядерного потока, без смены DPL, Esp и Es наверно не относятся к фрейму. Тогда почуму возникает исключение.
     
  3. je_

    je_ New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2004
    Сообщения:
    143
    вы должны создать в стеке всё нужноё для возврата. R3 cs:eip-ss:esp-eflags
    IRETD-у больше неотчего падать.
     
  4. Clerk

    Clerk Забанен

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

    je_ New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2004
    Сообщения:
    143
    когда происходит SYSENTER, надо создать всё (ну как INT делает).
    короче смотри win-SYSENTER-код
     
  6. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Я жду конкретной помощи, если не знаешь зачем отвечать тогда.
    Я создал всё - пул с обработчиком, 4 дескриптора. Ядерный Ss должен загружаться из мср только при выполнении Sysenter, смещение в сегменте валидное, обработчик не вызывается, а возникает #GP при возврате из аппаратного прерывания, причём Ss перезагружен из мср, я не понимаю как такое может быть вобще.
     
  7. je_

    je_ New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2004
    Сообщения:
    143
    CS база тоже 0
    все 4 селектора должны FLAT
     
  8. PSR1257

    PSR1257 New Member

    Публикаций:
    0
    Регистрация:
    30 ноя 2008
    Сообщения:
    933
    Сразу прошу на мои слова внимания много не обращать. Нельзя трассировать этот апаратный обработчик? Например через DRx поставить на его вход точку останова и смотреть последовательно - откуда пришло прерывание и с какими регистрами? Не совсем понятно куда собирается возвращаться управление по iretd - на 120:8057D695?
     
  9. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    PSR1257
    а вам зачем
     
  10. green

    green New Member

    Публикаций:
    0
    Регистрация:
    15 июл 2003
    Сообщения:
    1.217
    Адрес:
    Ukraine
    Clerk
    Думаю, где-то система предполагает нулевую базу cs-сегмента, т.е. что cs:addr и xx:addr адресуют одно и то же.
     
  11. je_

    je_ New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2004
    Сообщения:
    143
    если система работает на SYSENTER-е(а не на INT2e), то всякий user-mode SYSENTER будет делать 0D фаулт, а значит и ОСь помрёт (да и в стеке нечего не будет создано для IRETD нормального)
     
  12. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Не верно. Когда исполняется инструкция Sysenter выполняется перезагрузка селекторов. Загружаются ядерные селекторы в Ss и Cs. Смысл захвата - я решил отказаться от какихбыто нибыло жестких перехватов, даже более того, от регистрации диспетчера исключений. Селектор сегмента кода загружается из IA32_SYSENTER_CS. Селектор сегмента кода загружается на 1 больше, чем Cs тоесть индексирует следующий дескриптор в GDT. Для инструкции Sysexit юзермодные селекторы сегментов кода и стека загружаются как IA32_SYSENTER_CS +1, +2, тоесть индексируют два следующих дескриптора в GDT. Суть захвата: http://www.virustech.org/f/viewtopic.php?id=88
    Выполняется инструкция Sysenter, после перезагрузки Ss выполняется передача управления на обработчик, адрес его определён как сегмент:смещение, мы не изменяем указатель на обработчик в IA32_SYSENTER_EIP, а там по дефолту смещение в сегменте с базой ноль. База сегмента кода устанавливается как разность нашего обработчика в пуле минус смещение в сегменте с нулевой базой(ст. IA32_SYSENTER_EIP). Тоесть выполняется передача управления на адрес (Pool - IA32_SYSENTER_EIP):IA32_SYSENTER_EIP, при условии адрес пула с обработчиком(смещение в сенменте с базой ноль) > IA32_SYSENTER_EIP и гранулярность равна одному байту, ибо KiFastCallEntry не выравнена в памяти на 4 байта и может быть где угодно.
    После выполнения перехода на обработчик по исполнению Sysenter селектор стека индексирует дескриптор с нулевой базой(изменён только сегмент кода), поэтому никакие коллизии со стеком не должны возникнуть. Диспетчер восстанавливает дефолтный селектор сегмента кода, выполнив Retf.
    Касательно возврата из сервиса. Два вторых дескриптора имеют базы ноль и идентичны дефолтным в юзермоде(KGDT_R3_CODE or RPL_MASK etc). Это также не должно вызвать коллизий.

    Касательно отладки.
    - Сисер не может трассировать код, с изменёнными селекторами. Всё рушится либо ядро виснет изза отладчика. После установки перехвата выполняю трассировку до возврата из сервиса, соответственно на KiServiceExit(). Sysexit исполняется, меняется DPL и перезагружаются регистры как положено. Всё валидно, возврат разумеется на KiFastSystemCallRet. Там инструкция Ret, при попытке её исполнения всё рушится.
    - Виндбг на варе. Благо варя эмулирует эти 3 мср. Но это вобще ужас. Выполняем процедуру захвата. Следующая инструкция - мы попадаем на инструкцию Int3 гдето глубоко в недрах KeBugCheck, если зациклить обработчик ось виснет попытка останова приводит к темже результатам, невозможно дебажить.
     
  13. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    PSR1257
    В Ss не должно быть 120. Это значение будет загружено только при исполнении Sysenter. Так как эта инструкция выполняется со сброшенным TF, первое #GP - в при возврате из обработчика прерываний, а переход на обработчик не исполняется вовсе, то не понятно что приводит к его загрузке.
     
  14. PSR1257

    PSR1257 New Member

    Публикаций:
    0
    Регистрация:
    30 ноя 2008
    Сообщения:
    933
    Clerk

    Я имел в виду "отладку перочинным ножиком" - перехватить "перед самым концом" точку входа в этот обработчик прерывания (по идее просто jmp хватит) и вывести селектора, стек прямо в видеобуфер (придется описывать еще один дескриптор). Таким образом далее - если будет нужно - смотреть что происходит.
     
  15. je_

    je_ New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2004
    Сообщения:
    143
    Clerk, в вашем 1-ом посте я вижу:
    Код (Text):
    1. ;Sysenter CS
    2. +120        3AC9FFFF    ;Base: 0x1293AC9(+KiFastCallEntry:VALID)
    3. +124        014F9B29
    а INTEL (а не я!) пишет, все 4 сегмента должны быть FLAT, то_есть с нулувой базой
     
  16. je_

    je_ New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2004
    Сообщения:
    143
    ещё проще! (начинающим) скопируйте 64 байты от GDT+08 (где m$-имеет) на выделенное место в GDT. потом пиЩете MSR EIP ...
     
  17. je_

    je_ New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2004
    Сообщения:
    143
    отвлекли.. 32 байта нужно копировать.
    НО!

    у m$ там особая реализация SYSENTER-EIP кода. изза этого, не рекомендую переадресацию
    а лучше делать хук в коде ниже SYSENTER-EIP, а именно там, где начинается INЕ2e код.
    !! m$-SYSENTER-EIP просто является прологом к INЕ2e хендлеру
     
  18. je_

    je_ New Member

    Публикаций:
    0
    Регистрация:
    27 янв 2004
    Сообщения:
    143
    Код (Text):
    1. m$-SYSENTER-EIP
    2.  
    3. mov         ecx,ss:[0FFDFF040
    4. mov         esp,ss:[ecx][04]
    5. mov         ecx,07FFE0304
    6. cmp         esp,[0FFDFF004]
    7. je         DOS-case
    8. push        000000023
    9. push        edx
    10. add         edx,8
    11. push        000000202
    12. push        2
    13. popfd
    14. push        01B
    15. push        ecx
    16.  
    17. <<INT2e
    18. push        0
    19. push        ebp
    20. push        ebx
     
  19. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    je_
    На счёт генерации исключения изза не нулевой базы очень сомневаюсь.
    Что особенного ?
    Изменяем IA32_SYSENTER_EIP - фтопку такой перехват. Про жёсткий(ты наверное имеешь ввиду захват KiSystemServiceRepeat) вообще можно забыть.
    Что это за кусок кода и как он поможет решению моеё задачи ?
    Перестань флудить в моём топике, вообще попросим вас удалиться отсюда.
     
  20. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Я забыл про KiSystemCallExitBranch(). Инструкция Sysexit вызывается не только для возврата из сервисов, но и например для возврата на обработчик APC в юзермод и пр. Я забыл про это :dntknw:
    Сделал следующим образом. Отключил быстрые вызовы(в UsSystemCall указатель с KiFastSystemCall на KiIntSystemCall). Послу этого сервисы юзаются посредством 2e шлюза. Далее отключаю быстрый возврат(патчь KiSystemCallExitBranch на Iretd). Посылаем ипи, в нём захват на двух ядрах. После этих манипуляций ось работает нормально.
    #GP не возникает. Далее разрешаю быстрый возврат(восстановление смещения в опкоде по KiSystemCallExitBranch). Инструкция Sysexit отрабатывает нормально. Тоесть происходит возврат в юзермод, селектор сегмента кода загружается из мср. Далее всё работает, какойто тред выполняет возврат в юзермод, выполняется макрос EXIT_ALL. Тут кривая проверка, не по маске а сравнением селектора, если он не дефолтный то выполняется ветвление не туда, как для VDM процессов. Там портится стек и генерируется #GP. Тоесть до инструкции Sysenter дело не доходит, хотя как и в случае с Sysexit вероятно никаких исключений не будет, тоесть базу сегмента можно указать любую. Тоесть нельзя выполнять юзермодный код с селектором кодового сегмента отличным от 3.
    Перехват не будет работать без патча ядра. Думаю вопрос исчерпан.