Ниже по тексту описаны необычные свойства и особенности реального режима работы
процессора (Intel), секретные инструкции процессора, скрытые свойства известных инструкций. Среди свойств реального режима есть 32-битный сегмент кода в реальном режиме и прерывания в этом состоянии. Приведены 2-е программы на Ассемблере для перевода процессора в специальные состояния. Свойства сегментов при "абортном" возврате из защищённого режима. Нулевой дескриптор. Секреты инструкций bt / bts / btr / btc ,а также bswap / movsx / movzx / div / fbld / aaa / smsw / sal / test / fxch / fcom / fcomp / fstp /... И какие новые инструкции использует Windows XP. Результаты эксперимента со страничной трансляцией. Почти всё было проверено: мало на Pentium,
но больше на Pentium II, мало на Pentium III и всё на Pentium 4 (это домашний 1.8 GHz).
Рассчитано на подготовленного читателя знающего ассемблер и хоть немного владеющего понятиями защищённого режима работы процессора.
Далее обозначения: siz - неявный префикс с кодом 66h, переключает разрядность операндов для конкретной инструкции, addrsiz - неявный префикс с кодом 67h, переключает разрядность адресов для конкретной инструкции и кое что ещё.
В защищённом режиме сегменты могут иметь очень интересные и нужные свойства, оказывается при возврате в реальный режим их можно сохранить, не полностью, но
хотя бы частично, зато самые главные.
"Нереальный режим" / "Unreal Mode" / "Big Real Mode".
Обычный реальный режим с удлинёнными сегментами
es, ds, fs, gs. И любым значением бита разрядности
(смещения 00000000--FFFFFFFF). Если они расширяемы вверх, то бит разрядности может быть любым, а если вниз, то этот бит определяет верхнюю границу сегмента! Причём ss удлинять можно только оставляя его 16-ти битным. Иначе режим станет несовместим с DOS. Точнее почти совместим при условии, что esp[31..16]=0 всегда, но этого нельзя гарантировать (и не использовать leave / см. ниже). Ведь все стековые инструкции станут использовать esp! А cs удлинять можно обязательно оставляя его 16-ти битным. В противном случае это приведёт к изменению разрядности, об этом ниже. Получится "Фантастический режим". Правда для всех сегментных регистров можно установить базу какую угодно
(например 4000000h), но это до первой загрузки любого значения в сегментный регистр, которое и станет новой базой после сдвига влево на 4-ре. Лимит и атрибуты сохранятся!
(himem.sys делал именно так: он устанавливал большую базу для доступа к далёкой памяти с помощью недокументированной инструкции loadall (уже не поддерживается), далее делал что надо и записывал в сегментный регистр его исходное значение, восстанавливая тем самым его базу). Примечание: если удлинить cs (оставив бит разрядности 0) и попытаться исполнить инструкцию перехода с префиксом siz, то 32-х битное смещение перехода проверится на попадание в этот сегмент и всё равно при переходе старшие биты 31..16 проигнорируются, так как используется ip. (И даже не запишутся в
eip[31..16]!?) Ниже приведена программа на Ассемблере для удлинения
es, ds, fs, gs.
;(tasm and tlink version 2.0)
;tasm.exe bigreal.asm
;tlink.exe bigreal.obj /t /3
;bigreal.com
;
.486p
cseg segment use16 para
assume es:cseg
assume cs:cseg
assume ss:cseg
assume ds:cseg
org 100h
SELECTOR_DS32 equ 8
start:
;-------------------------------------------
mov eax,cs
shl eax,4
add dword ptr GDTR+2,eax
in al,92h
or al,2
out 92h,al
cli
in al,70h
or al,80h
out 70h,al
xor edx,edx
mov dl,SELECTOR_DS32
lgdt fword ptr GDTR
mov eax,cr0
or al,1
mov cr0,eax
mov es,dx
mov ds,dx
mov fs,dx
mov gs,dx
and al,-2
mov cr0,eax
mov ax,cs
mov es,ax
mov ds,ax
xor ax,ax
mov fs,ax
mov gs,ax
in al,70h
and al,7Fh
out 70h,al
sti
;Out to screen buffer (fs=0)
;
; mov ecx,0B8000h+80*4
; mov dword ptr fs:[ecx],0F300F32h
retn
;---
align 8
GDTR dw 15
dd offset GDT
dw -1
GDT db 0,0,0,0,0,0,0,0
db 0FFh,0FFh,0,0,0,092h,0CFh,0
;---
cseg ends
end start
Перезаписывать es, ds, fs, gs можно сколько угодно. Записанное значение сдвинутое влево на 4-е станет новой базой сегмента. А лимит сегмента останется прежним (т.е. большим).
Фантастический режим работы процессора "FANTASY MODE" / "Real32 Mode".
Переход в него происходит так: Переход в полноценный 32-й защищённый режим. Такой, где все сегментные регистры получают предел 0FFFFFFFFh, становятся 32-х битными, данные расширяемы вверх и доступны на запись, а код не подчинённым и доступным для чтения. Текущая разрядность операндов становится
32 (т.к. трогали cs). А стековые инструкции теперь по умолчанию используют
esp (т.к. трогали ss). После этого сразу переход в реальный режим (cr0.pe=0) с сохранением всех хороших свойств сегментов. Теперь находимся в режиме, похожем одновременно на реальный и тот, что используют современные приложения для Windows. Если теперь записать что-нибудь в какой-нибудь сегментный регистр
(es, cs, ss, ds, fs, gs / в cs пишем far инструкциями), то это значение сдвинутое влево на 4-е станет новой базой сегмента, а его предел не изменится (вот это хорошо!). Как в обычном реальном режиме. И это можно делать сколько угодно раз! При этом код декодируется и исполняется как 32-х битный. Например:
33 C0 xor eax,eax
BF AB CD EF 12 mov edi,12EFCDABh
8D 4E 04 lea ecx,[esi+4]
54 push esp
Нет префиксов 66/67 (siz/addrsiz) для доступа к большим регистрам или расширенной адресации, они остались для доступа к 2-х байтным. Это 32-х разрядный код, а ведь cr0.pe=0 <- реальный режим. И все сегменты бесконечны! Код может исполняться в любом месте адресного пространства. Ниже приведена программа на Ассемблере для перехода в этот режим.
;(tasm and tlink version 2.0)
;tasm.exe real32.asm /m2
;tlink.exe real32.obj /3
;real32.exe
;
.486p
cseg16 segment use16
assume cs:cseg16
assume ss:sseg
assume ds:dseg
assume es:dseg
assume fs:nothing
assume gs:nothing
;--- REAL MODE 16 BIT (NORMAL) ---
start:
xor eax,eax
mov ax,dseg
mov ds,ax
mov es,ax
shl eax,4
add dword ptr GDTR+2,eax
mov word ptr DS32+2,ax
mov word ptr DS16+2,ax
bswap eax
mov byte ptr DS32+4,ah
mov byte ptr DS16+4,ah
xor eax,eax
mov ax,cseg32
shl eax,4
mov word ptr CS32+2,ax
bswap eax
mov byte ptr CS32+4,ah
mov eax,cs
shl eax,4
mov word ptr CS16+2,ax
bswap eax
mov byte ptr CS16+4,ah
mov eax,ss
shl eax,4
mov word ptr SS16+2,ax
mov word ptr SS32+2,ax
bswap eax
mov byte ptr SS16+4,ah
mov byte ptr SS32+4,ah
in al,92h
or al,2
out 92h,al
cli
in al,70h
or al,80h
out 70h,al
lgdt fword ptr GDTR
mov eax,cr0
or al,1
mov cr0,eax
jmp fword ptr PM32_ENTRY
;--- PROTECTED MODE 16 BIT ---
__PM16_ENTRY:
mov ax,SELECTOR_DS16
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ax,SELECTOR_SS16
mov ss,ax
mov eax,cr0
and al,-2
mov cr0,eax
jmp fword ptr RM16_ENTRY
;--- REAL MODE 16 BIT (NORMAL) ---
__RM16_ENTRY:
mov ax,dseg
mov ds,ax
mov es,ax
mov ax,sseg
mov ss,ax
xor ax,ax
mov fs,ax
mov gs,ax
in al,70h
and al,7Fh
out 70h,al
sti
mov ax,4C00h
int 21h
cseg16 ends
cseg32 segment use32
assume cs:cseg32
assume ss:sseg
assume ds:dseg
assume es:dseg
assume fs:nothing
assume gs:nothing
;--- PROTECTED MODE 32 BIT ---
__PM32_ENTRY:
mov eax,SELECTOR_DS32
mov ds,eax
mov es,eax
mov eax,SELECTOR_FS32
mov fs,eax
mov gs,eax
mov eax,SELECTOR_SS32
mov ss,eax
and esp,0FFFFh
mov eax,cr0
and al,-2
mov cr0,eax
jmp fword ptr [large offset RM32_ENTRY]
;--- REAL MODE 32 BIT (FANTASY MODE) ---
__RM32_ENTRY:
xor eax,eax
mov ax,dseg
mov ds,eax
mov es,eax
mov ax,sseg
mov ss,eax
xor eax,eax
mov fs,eax
mov gs,eax
;------------------------------
;(Default operation size is 32 in real mode !!!)
;Work in fantasy mode ...
;Выводим сообщение на экран
mov edi,0B8660h
mov esi,offset __fantasy
mov ah,0Fh
__copy:
mov al,[esi]
or al,al
jz short __stop
inc esi
mov gs:[edi],ax ;gs=0 !!!
inc edi
inc edi
jmp __copy
__stop:
;-------
;Interrupt
; Подвинем табличку прерываний в другое место.
sidt fword ptr [large offset old_idt]
lidt fword ptr [large offset new_idt]
; Считаем в регистр значение его базового адреса.
mov edx,dword ptr [large offset new_idt+2]
; Установим новый стек и сохраним старый.
mov ebp,esp
mov esp,4000000h
mov ecx,fs:[edx] ; это на всякий случай
; Утанавливаем обработчик исключения деления на ноль. Fault
#DE.
mov word ptr fs:[edx],offset __the_handler
mov fs:[edx+2],cs
; Генерируем Fault #DE.
div edx ;Fault #DE.
mov fs:[edx],ecx ; это на всякий случай вернём на место
; Восстанавливаем стек и возвращаем таблицу прерываний на место.
mov esp,ebp
lidt fword ptr [large offset old_idt]
;------------------------------
mov eax,cr0
or al,1
mov cr0,eax
jmp fword ptr [large offset PM16_ENTRY]
;-------
__the_handler:
; это чтобы пропустить div edx
add word ptr [esp],2
push eax
push esi
push edi
push gs
xor eax,eax
mov gs,eax
mov edi,0B8700h
mov esi,offset __Int_0
mov ah,0Fh
; Выведем сообщение.
___copy:
mov al,[esi]
or al,al
jz short ___stop
inc esi
mov gs:[edi],ax
inc edi
inc edi
jmp ___copy
___stop:
pop gs
pop edi
pop esi
pop eax
iret ;66 CF должно быть так !!!
;------------------------------
cseg32 ends
dseg segment use16 para
;-----------------
PM32_ENTRY label fword
dd offset __PM32_ENTRY
dw SELECTOR_CS32
RM32_ENTRY label fword
dd offset __RM32_ENTRY
dw cseg32
PM16_ENTRY label fword
dd offset __PM16_ENTRY
dw SELECTOR_CS16
RM16_ENTRY label fword
dd offset __RM16_ENTRY
dw cseg16
;-----------------
GDTR dw Limit_GDT
dd offset GDT
;-----------------
SELECTOR_CS32 EQU 8
SELECTOR_DS32 EQU 16
SELECTOR_SS32 EQU 24
SELECTOR_FS32 EQU 32
SELECTOR_CS16 EQU 40
SELECTOR_DS16 EQU 48
SELECTOR_SS16 EQU 56
;-----------------
align 8
GDT dq 0
CS32 db 0FFh,0FFh,0,0,0,09Ah,0CFh,0
DS32 db 0FFh,0FFh,0,0,0,092h,0CFh,0
SS32 db 0FFh,0FFh,0,0,0,092h,0CFh,0
FS32 db 0FFh,0FFh,0,0,0,092h,0CFh,0
CS16 db 0FFh,0FFh,0,0,0,09Ah,0,0
DS16 db 0FFh,0FFh,0,0,0,092h,0,0
SS16 db 0FFh,0FFh,0,0,0,092h,0,0
;-----------------
Limit_GDT=$-GDT-1
;-----------------
__fantasy db 'Hello from "FANTASY MODE"/"Real32 Mode".',0
__Int_0 db "Hello from #DE exception handler.",0
;-----------------
align 8
new_idt label fword
dw 32*4-1
dd 2000000h ; табличка прерываний за 32 мегабайтом.
align 8
old_idt df ?
dseg ends
sseg segment use16 stack
db 400h dup (?)
sseg ends
end start
Стек в "FANTASY MODE"/"Real32 Mode".
Стек теперь работает с регистром esp и может быть размещён в любом свободном месте адресного пространства. Абсолютно все стековые инструкции обращающиеся к нему используют esp.
Таблица векторов прерываний в "FANTASY MODE" / "Real32 Mode".
Таблица векторов прерываний имеет тот же формат,
что и в нормальном реальном режиме. Один вектор - это
4 байта: младшие 2 это смещение, а старшие 2 сегмент. И так же, как и в реальном режиме, она может быть размещена где угодно в памяти. Хоть за 128-м мегабайтом. Она так же может быть уменьшена. Всё это достигается при помощи инструкции lidt. В этом режиме ей не нужен префикс siz чтобы использовать старший байт базы.
Инструкция int в "FANTASY MODE"/"Real32 Mode".
Инструкция int выполняется и вообще прерывания точно так же, как и в нормальном реальном режиме. Точно так же в стек помещаются
2 байта флагов, 2 байта регистра cs и 2 байта смещения возврата. Ровно
6 байтов. Далее некоторые флажки принудительно обнуляются (ну как обычно), в регистр cs заносится значение сегмента из таблицы, а в регистр eip заносится значение смещения из таблицы, расширенное нулём до 4-х байтов. Инструкция int может быть выполнена в любом месте адресного пространства. Но к сожалению, если она далеко (~1 M), то она не оставляет достаточно информации для возврата обратно. Префиксы siz/addrsiz игнорируются. Возврат осуществляется инструкцией
iret (66 CF / т.к. режим 32-х разрядный, а нужна 16-я
iret).
"Нулевой дескриптор" при "абортном возврате" из защищённого режима.
Если при работе в защищённом режиме загрузить в сегментный регистр нулевое значение (нулевой селектор / первые 8-мь байт ГДТ нули), то при возврате в реальный режим любые инструкции обращения к памяти с помощью этого сегмента будут вызывать исключение #GP.
xor bx,bx
mov ax,fs:[bx] ;<-fault #GP
При этом в эти сегментные регистры можно писать и их можно читать, но это не поможет. Независимо от значения занесённого в сегментный регистр инструкции написанные выше сработают
одинаково. И игнорируется даже содержимое нулевого дескриптора в таблице. Исключение #GP всё равно возникает. При этом адрес возврата указывает на "плохую инструкцию". (в стек заносится 6-ть байтов и выносится тоже 6-ть
iret).
(Продолжение следует)