Всем привет. Такая проблема. Захват Sysenter. В IA32_SYSENTER_CS загружается селектор, индексирующий ядерный дескриптор с базой, пересчитанной как (DispatcherEntry[в неподкачиваемом пуле] - IA32_SYSENTER_EIP[KiFastCallEntry]), гранулярность - 1. Рядом в GDT создаются есчо три дескриптора(требует Sysenter/Sysexit), второй для ядерного сегмента стека, третий и четвёртый соответственно юзермодные сегменты кода и стека. Три последних сегмента имеют нулевые базы, дескрипторы аналогичные как по умолчанию: Код (Text): GDT; ;Sysenter CS +120 3AC9FFFF ;Base: 0x1293AC9(+KiFastCallEntry:VALID) +124 014F9B29 ;Sysenter Ss +128 0000FFFF ;Base: 0 +12C 00CF9300 ;Sysexit Cs +130 0000FFFF ;Base: 0 +134 00CFFB00 ;Sysexit Cs +138 0000FFFF ;Base: 0 +13C 00CFF300 В мср: IA32_SYSENTER_EIP: ntoskrnl!KiFastCallEntry IA32_SYSENTER_CS: 0x120 В IA32_SYSENTER_ESP дефолтный указатель на DPC-стек. P4, второе ядро отключено(в бутини). База не менялась: Код (Text): GDTR: Base 8003F000 Limit 000003FF Собственно код: ftp://files.virustech.org/Code/Sysenter/CsBase/ Рушится на #GP(KiTrap0D). Стек: Код (Text): ErrorCode 00000000 Eip 804DE36F ;Kei386EoiHelper -> IRetd Cs 00000008 ;KGDT_R0_CODE EFlags 00010082 Esp 8057D695 ;ntoskrnl!CcMapData + 0xCB Ss 00000120 Исключение возникает в Kei386EoiHelper() на инструкции IRetd, видимо при обработке аппаратного прерывания, при возврате на прерванный код. Не валидный указатель на стек, указывает внутрь ядра(не доступна для записи страница), селектор сегмента стека изменённый 0x120(база нулевая, лимит -1). Не могу понять почему рушится и откуда изменяется указатель на стек, сутки ковырял и ничего.
Хотя нет, прерывание ядерного потока, без смены DPL, Esp и Es наверно не относятся к фрейму. Тогда почуму возникает исключение.
вы должны создать в стеке всё нужноё для возврата. R3 cs:eip-ss:esp-eflags IRETD-у больше неотчего падать.
Я жду конкретной помощи, если не знаешь зачем отвечать тогда. Я создал всё - пул с обработчиком, 4 дескриптора. Ядерный Ss должен загружаться из мср только при выполнении Sysenter, смещение в сегменте валидное, обработчик не вызывается, а возникает #GP при возврате из аппаратного прерывания, причём Ss перезагружен из мср, я не понимаю как такое может быть вобще.
Сразу прошу на мои слова внимания много не обращать. Нельзя трассировать этот апаратный обработчик? Например через DRx поставить на его вход точку останова и смотреть последовательно - откуда пришло прерывание и с какими регистрами? Не совсем понятно куда собирается возвращаться управление по iretd - на 120:8057D695?
Clerk Думаю, где-то система предполагает нулевую базу cs-сегмента, т.е. что cs:addr и xx:addr адресуют одно и то же.
если система работает на SYSENTER-е(а не на INT2e), то всякий user-mode SYSENTER будет делать 0D фаулт, а значит и ОСь помрёт (да и в стеке нечего не будет создано для IRETD нормального)
Не верно. Когда исполняется инструкция 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, если зациклить обработчик ось виснет попытка останова приводит к темже результатам, невозможно дебажить.
PSR1257 В Ss не должно быть 120. Это значение будет загружено только при исполнении Sysenter. Так как эта инструкция выполняется со сброшенным TF, первое #GP - в при возврате из обработчика прерываний, а переход на обработчик не исполняется вовсе, то не понятно что приводит к его загрузке.
Clerk Я имел в виду "отладку перочинным ножиком" - перехватить "перед самым концом" точку входа в этот обработчик прерывания (по идее просто jmp хватит) и вывести селектора, стек прямо в видеобуфер (придется описывать еще один дескриптор). Таким образом далее - если будет нужно - смотреть что происходит.
Clerk, в вашем 1-ом посте я вижу: Код (Text): ;Sysenter CS +120 3AC9FFFF ;Base: 0x1293AC9(+KiFastCallEntry:VALID) +124 014F9B29 а INTEL (а не я!) пишет, все 4 сегмента должны быть FLAT, то_есть с нулувой базой
ещё проще! (начинающим) скопируйте 64 байты от GDT+08 (где m$-имеет) на выделенное место в GDT. потом пиЩете MSR EIP ...
отвлекли.. 32 байта нужно копировать. НО! у m$ там особая реализация SYSENTER-EIP кода. изза этого, не рекомендую переадресацию а лучше делать хук в коде ниже SYSENTER-EIP, а именно там, где начинается INЕ2e код. !! m$-SYSENTER-EIP просто является прологом к INЕ2e хендлеру
Код (Text): m$-SYSENTER-EIP mov ecx,ss:[0FFDFF040 mov esp,ss:[ecx][04] mov ecx,07FFE0304 cmp esp,[0FFDFF004] je DOS-case push 000000023 push edx add edx,8 push 000000202 push 2 popfd push 01B push ecx <<INT2e push 0 push ebp push ebx
je_ На счёт генерации исключения изза не нулевой базы очень сомневаюсь. Что особенного ? Изменяем IA32_SYSENTER_EIP - фтопку такой перехват. Про жёсткий(ты наверное имеешь ввиду захват KiSystemServiceRepeat) вообще можно забыть. Что это за кусок кода и как он поможет решению моеё задачи ? Перестань флудить в моём топике, вообще попросим вас удалиться отсюда.
Я забыл про KiSystemCallExitBranch(). Инструкция Sysexit вызывается не только для возврата из сервисов, но и например для возврата на обработчик APC в юзермод и пр. Я забыл про это Сделал следующим образом. Отключил быстрые вызовы(в UsSystemCall указатель с KiFastSystemCall на KiIntSystemCall). Послу этого сервисы юзаются посредством 2e шлюза. Далее отключаю быстрый возврат(патчь KiSystemCallExitBranch на Iretd). Посылаем ипи, в нём захват на двух ядрах. После этих манипуляций ось работает нормально. #GP не возникает. Далее разрешаю быстрый возврат(восстановление смещения в опкоде по KiSystemCallExitBranch). Инструкция Sysexit отрабатывает нормально. Тоесть происходит возврат в юзермод, селектор сегмента кода загружается из мср. Далее всё работает, какойто тред выполняет возврат в юзермод, выполняется макрос EXIT_ALL. Тут кривая проверка, не по маске а сравнением селектора, если он не дефолтный то выполняется ветвление не туда, как для VDM процессов. Там портится стек и генерируется #GP. Тоесть до инструкции Sysenter дело не доходит, хотя как и в случае с Sysexit вероятно никаких исключений не будет, тоесть базу сегмента можно указать любую. Тоесть нельзя выполнять юзермодный код с селектором кодового сегмента отличным от 3. Перехват не будет работать без патча ядра. Думаю вопрос исчерпан.