• Партнер

  • Ниже по тексту описаны необычные свойства и особенности реального режима работы
    процессора (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).

    (Продолжение следует)

    Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии