Защищённый режим процессоров Intel 80286/80386/80486
9. ПРИЛОЖЕНИЕ
9.1. Регистр EFLAGS
9.2. Управляющие регистры процессора i80386
9.3. Формат регистра CR0 процессора i80386
9.4. Формат регистра CR0 процессора i80486
9.5. Формат регистра CR3 процессора i80486
9.6. Системные команды процессоров i80286/i80386/i80486
9.7. Недокументированная команда LOADALL
9.8. Утилита MEMOSCOP
9.9. Защита программ от отладки
В этой таблице описан формат регистра флагов для процессоров i80386
и i80486 (регистр флагов процессора i80286 называется FLAGS и
представляет собой младшее 16-разрядное слово регистра EFLAGS):
Номер бита | Назначение |
0 - CF | Флаг переноса |
1 - 1 | Зарезервировано и равно 1
|
2 - PF | Флаг чётности |
3 - 0 | Зарезервировано и равно 0
|
4 - AF | Флаг вспомогательного переноса
|
5 - 0 | Зарезервировано и равно 0
|
6 - ZF | Флаг нуля |
7 - SF | Флаг знака |
8 - TF | Флаг ловушки |
9 - IF | Флаг разрешения прерываний
|
10 - DF | Флаг направления |
11 - OF | Флаг переполнения
|
12-13 - IOPL | Уровень привилегий ввода/вывода
|
14 - NT | Флаг вложенной задачи
|
15 - 0 | Зарезервировано и равно 0
|
16 - RF | Флаг возобновления (только i80386 и i80486)
|
17 - VM | Флаг режима виртуального процессора 8086 (только i80386 и i80486)
|
18 - AC | Флаг проверки выравнивания (только i80486)
|
19-31 - 0 | Зарезервировано и равно 0
|
Регистр | Назначение |
CR0 | Регистр состояния процессора
|
CR1 | Зарезервирован |
CR2 | Линейный адрес отказа страницы
|
CR3 | Базовый адрес каталога страницы
|
Номер бита | Назначение |
0 - PE | Включение защищённого режима работы процессора.
|
1 - MP | Присутствие сопроцессора.
|
2 - EM | Эмуляция сопроцессора.
|
3 - TS | Переключение задачи.
|
4 - ET | Тип сопроцессора - i80287 или i80387.
|
5-14 | Зарезервировано |
15 - PG | Включение механизма трансляции страниц
|
Номер бита | Назначение |
0 - PE | Включение защищённого режима работы процессора.
|
1 - MP | Присутствие сопроцессора.
|
2 - EM | Эмуляция сопроцессора.
|
3 - TS | Переключение задачи.
|
4 - ET | Тип сопроцессора - i80287 или i80387.
|
5 - NE | Числовая ошибка. Разрешает обработку ошибок при операциях с плавающей точкой.
|
6-15 | Зарезервировано |
16 - WP | Защита записи. При установке этого бита страницы пользователя защищены от записи в режиме супервизора.
|
17 | Зарезервировано |
18 - AM | Бит маски выравнивания. Этот бит разрешает или запрещает контроль выравнивания операндов команд в памяти. Контроль выравнивания разрешён только для программ, работающих в третьем кольце, при условии что установлен бит AM.
|
19-28 | Зарезервировано |
29 - NW | Разрешение сквозной записи. Используется в механизме управления кэшированием.
|
30 - CD | Запрещение кэширования. Если этот бит установлен, внутреннее кэширование запрещено.
|
31 - PG | Включение механизма трансляции страниц
|
Номер бита | Назначение |
0-2 | Зарезервировано |
3 - PWT | Прозрачность записи на уровне страниц. Используется для управления записью во внешний кэш.
|
4 - PCD | Запрещение кэширования на уровне страниц. Используется для управления работой внешнего кэша.
|
5-31 - PBDR | Базовый регистр каталога страниц
|
Системные команды предназначены для использования, главным образом,
в модулях операционных систем (в модулях ядра операционной системы,
в драйверах и т.д.). Некоторые из перечисленных ниже команд полезны
и при разработке прикладных программ, работающих в защищённом
режиме. Мы приведём только краткий перечень основных системных
команд, подробности вы можете узнать из справочных руководств
по процессорам (см. список литературы).
Как правило, системные команды могут использовать только те программы,
которые выполняются в нулевом привилегированном кольце.
ARPL Коррекция поля привилегий инициатора запроса в селекторе
Эта команда используется системными модулями для проверки уровня
запрашиваемых привилегий в передаваемых им в качестве параметров
селекторов. Прикладная программа не должна запрашивать привилегии,
превышающие её собственные.
Первый операнд команды - 16-разрядный регистр или слово памяти,
содержащие значение проверяемого селектора. Второй операнд - регистр,
в который записано содержимое CS прикладной программы.
Если команда не изменяла уровень привилегий, в регистре FLAGS
(EFLAGS для процессоров i80386 и i80486) устанавливается флаг
нуля. В противном случае этот флаг сбрасывается.
Пример использования команды:
mov dx, cs
mov ax, TESTED_SELECTOR
arpl dx, ax
CLTS Сброс флага TS переключения задачи в регистре CR0
Каждый раз при переключении задачи флаг TS устанавливается в 1.
Команда CLTS позволяет сбросить этот флаг.
LAR Загрузка байта прав доступа
Для процессора i80286 команда LAR загружает в первый операнд (регистр)
байт доступа дескриптора, выбираемого вторым операндом. Второй
операнд является селектором, указывающим на используемый дескриптор.
В процессорах i80386 и i80486 команда LAR использует в качестве
первого операнда 32-разрядный регистр. Кроме байта прав доступа
в этот регистр заносятся биты типа сегмента (9-11), DPL (14),
бит присутствия (15), бит дробности (23).
LGDT Загрузка регистра GDTR
Команда выполняет инициализацию регистра GDTR, указывающего расположение
в памяти и размер глобальной таблицы дескрипторов.
LIDT Загрузка регистра IDTR
Команда выполняет инициализацию регистра IDTR, указывающего расположение
в памяти и размер дескрипторной таблицы прерываний.
LLDT Загрузка регистра LDTR
Команда выполняет инициализацию регистра LDTR, указывающего расположение
в памяти и размер локальной таблицы дескрипторов.
LMSW Загрузка слова состояния процессора
С помощью этой команды можно выполнить загрузку младшего слова
регистра CR0 из регистра - операнда команды.
Эта команда может использоваться для переключения процессора в
защищённый режим. Обратного переключения эта команда не обеспечивает
(даже для процессоров i80386 и i80486).
LSL Загрузка предела сегмента
Команда имеет два операнда. Граница сегмента, селектор которого
используется в качестве второго операнда (задаётся в регистре),
загружается в регистр, указанный в качестве первого операнда.
LTR Загрузка регистра задачи
Команда предназначена для загрузки регистра TR - регистра задачи.
Загрузка этого регистра не приводит к переключению задачи.
MOV Загрузка системных регистров
Для процессоров i80386 и i80486 в качестве операндов обычной команды
MOV допустимо (на нулевом уровне привилегий) указывать системные
регистры - CR0, CR2, CR3, DR0, DR1, DR2, DR3, DR6, DR7, TR6, TR7.
Команда MOV может быть использована процессорами i80386 и i80486
для возврата процессора из защищённого режима в реальный.
SGDT Запись в память содержимого регистра GDTR
Команда позволяет узнать текущее содержимое регистра глобальной
дескрипторной таблицы GDTR, обычное её используют в системных
отладчиках.
SIDT Записать в память содержимое регистра IDTR
Команда позволяет узнать текущее содержимое регистра глобальной
дескрипторной таблицы прерываний IDTR, используется в системных
отладчиках.
SLDT Записать в память содержимое регистра LDTR
Команда позволяет узнать текущее содержимое регистра локальной
дескрипторной таблицы LDTR, используется в системных отладчиках.
SMSW Записать слова состояния процессора
Команда записывает в память или 16-битовый регистр младшее слово
регистра CR0 и может быть использована в системных отладчиках.
STR Запись регистра задачи
Команда записывает текущее содержимое регистра задачи TR в 16-разрядную
ячейку памяти или 16-разрядный регистр. Может использоваться в
системных отладчиках.
VERR Проверить сегмент на возможность чтения
VERW Проверить сегмент на возможность записи
С помощью этих двух команд можно проверить доступность выбранного
селектором сегмента на чтение и запись, соответственно. Если операция
чтения или записи доступна, флаг нуля ZF устанавливается в единицу,
в противном случае он сбрасывается в ноль.
Основное назначение этой команды - предотвратить возникновение
исключения по защите памяти при попытке обращения к сегменту.
Прежде чем выполнять обращение, программа может проверить доступность
сегмента и сделать соответствующие выводы.
Оказывается, для процессора i80286 существует способ получения
доступа к расширенной памяти, не переключаясь в защищённый режим.
Для этого может быть использована недокументированная команда
LOADALL, имеющая код 0F05h (команда не имеет операндов). Эта команда
не описана в справочниках по процессору i80286, информация о ней
поставляется фирмой Intel по запросу. Те сведения о команде LOADALL,
которые приведены в нашей книге, получены по электронной почте
из BBS и могут быть использованы только для расширения вашего
кругозора и для оценки полезности этой команды в ваших разработках.
Команда LOADALL первоначально была задумана фирмой Intel как тестовая.
Однако оказалось, что она пригодна и для обращения к расширенной
памяти в реальном режиме. Широко известный драйвер расширенной
памяти HIMEM.SYS обращается в область адресов выше первого мегабайта
именно с помощью команды LOADALL (а не переключаясь в защищённый
режим и возвращаясь обратно, как это можно было бы предположить).
Команда LOADALL сокращает время, требуемое драйверу HIMEM.SYS
на доступ к расширенной памяти, так как время на переключение
в защищённый режим и обратное переключение достаточно велико по
сравнению с временем, необходимым на копирование данных из основной
памяти в расширенную или обратно.
Другое применение команды - драйвер электронного диска Microsoft
RAMDRIVE.SYS и блок совместимости операционной системы Microsoft
OS/2 версии 1.x.
Секрет команды LOADALL заключается в том, что она загружает ВСЕ
регистры процессора, и может выполняться в реальном режиме. Изменяя
поле базы регистра кэша дескриптора (внутренний системный регистр
процессора) программа может обратиться к сегменту, лежащему за
пределами первого мегабайта адресного пространства.
Как мы уже говорили, команда LOADALL не имеет операндов. Регистры
загружаются из буфера, который имеет длину 102 байта и должен
быть подготовлен в области памяти с физическим адресом 00800h.
Формат буфера представлен в следующей таблице:
Таблица 16. Формат буфера для команды LOADALL.
Адрес | Регистры процессора
|
800h-805h | Не используется
|
806h-807h | Слово состояния процессора MSW (Machine Status Word)
|
808h-815h | Не используется
|
816h-817h | Регистр задачи TR (Task Register)
|
818h-819h | Регистр флагов |
81Ah-81Bh | Регистр IP (Instruction Pointer)
|
81Ch-81Dh | Селектор LDT (Local Descriptor Table)
|
81Eh-81Fh | Регистр DS (Data Segment Selector)
|
820h-821h | Регистр SS (Stack Segment Selector)
|
822h-823h | Регистр CS (Code Segment Selector)
|
824h-825h | Регистр ES (Extra Segment Selector)
|
826h-827h | Регистр DI (Destination Index)
|
818h-829h | Регистр SI (Source Index)
|
82Ah-82Bh | Регистр BP (Base Pointer)
|
82Ch-82Dh | Регистр SP (Stack Pointer)
|
82Eh-82Fh | Регистр BX (Data Register BX)
|
830h-831h | Регистр DX (Data Register DX)
|
832h-833h | Регистр CX (Data Register CX)
|
834h-835h | Регистр AX (Accumulator)
|
836h-83Bh | Кэш дескриптора ES
|
83Ch-841h | Кэш дескриптора CS
|
842h-847h | Кэш дескриптора SS
|
848h-84Dh | Кеш дескриптора DS
|
84Eh-853h | Регистр GDTR (Global Descriptor Table Register)
|
854h-859h | Кэш дескриптора LDT
|
85Ah-85Fh | Регистр IDTR (Interrupt Descriptor Table Register)
|
860h-865h | Кэш дескриптора TSS (Task State Segment)
|
Для ускорения доступа к содержимому дескрипторных таблиц в процессоре
имеются так называемые теневые регистры или регистры кэша дескрипторов.
Когда процессор загружает селектор в сегментный регистр, автоматически
выполняется загрузка соответствующего регистра кэша дескриптора.
Не существует какого-либо иного способа загрузить кэш дескриптора
явно из программы с помощью обычных команд. Однако вы можете воспользоваться
для этого командой LOADALL, подготовив в описанном выше буфере
необходимые значения.
Формат кэша дескриптора приведён в следующей таблице:
Таблица 17. Формат кэша дескриптора.
Смещение поля | Назначение поля
|
0-2 | 24-битовый базовый адрес сегмента
|
3 | Байт доступа, его формат полностью аналогичен формату байта доступа дескриптора, за исключением бита присутствия. На месте этого бита находится бит VALID. Если этот бит сброшен в 0, при попытке использовать дескриптор для адресации памяти произойдёт исключение 13 с кодом ошибки 0.
|
4-5 | 16-битовый предел сегмента
|
Можно предложить следующий алгоритм использования команды LOADALL:
- Запретитите прерывания.
- Сохраните где-нибудь в буфере программы область памяти, начинающуюся
с адреса 00800h и имеющую длину 102 байта.
- Заполните буфер для команды LOADALL необходимыми значениями
для всех загружаемых регистров. Базовый адрес в области кэша дескриптора
сегмента данных должен указывать на необходимый вам участок расширенной
памяти.
- Выполните команду LOADALL. Сегмент данных теперь будет указывать
на область расширенной памяти.
- Выполните запись или чтение области расширенной памяти.
- Восстановите базовый адрес сегмента данных в кэше дескриптора
данных в буфре, расположенном по адресу 00800h.
- Выполните команду LOADALL ещё раз.
- Восстановите содержимое сохранённого ранее буфера.
- Разрешите прерывания.
При выполнении команды LOADALL не делается никаких проверок. Вам
необходимо самим позаботиться о том, чтобы загружаемые в регистры
процессора значения имели какой-нибудь смысл. В противном случае
состояние процессора окажется непредсказуемым.
Команда LOADALL может выполняться в защищённом режиме в нулевом
приоритетном кольце. Но, к сожалению, эту команду нельзя использовать
для переключения процессора из защищённого в реальный режим.
Процессор i80387 также имеет команду LOADALL, но её код и выполняемые
функции другие.
Для определения активных интерфейсов с защищённым режимом можно
использовать предлагаемую утилиту MEMOSCOP. Эта утилита проверяет
присутствие всех уровней поддержки программ, работающих в защищённом
режиме или с расширенной памятью - от BIOS до DPMI.
*MEMOSCOP*, © Frolov A.V., 1992
--------------------------------------------------------
Файл memoscop.c
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
void main(void) {
extern int getcpu(void);
unsigned cpu_type, ver;
unsigned err;
char ver_hi, ver_lo, verems;
unsigned hostdata_seg, hostdata_size, dpmi_flags;
void (far *pm_entry)();
union REGS regs;
struct SREGS segregs;
printf("n*MemoScop* v 1.0, © Frolov A.V., 1992n"
"---------------------------------------n");
// Определяем тип центрального процессора. Если программа
// работает на процессоре i8086, завершаем выполнение,
// так как в этом случае интерфейсы с защищенным режимом
// недоступны.
printf("Тип процессора: 80%dn", (cpu_type = getcpu()));
if(cpu_type == 86) {
printf("nНа этом процессоре нам работать не интересно...");
exit(0);
}
// Определяем размер доступной через прерывание INT 15h
// расширенной памяти.
printf("n------------------------ Уровень BIOS -------------------------n");
regs.h.ah = 0x88;
int86(0x15, ®s, ®s);
printf("Размер расширенной памяти, доступной через INT 15:"
" t%d Кбайтn", regs.x.ax);
// Проверяем, установлен ли драйвер HIMEM.SYS,
// если установлен, выводим его версию.
printf("n------------------------ Уровень XMM --------------------------n");
if (XMM_Installed()) {
printf("Установлен драйвер HIMEM.SYS");
ver = XMM_Version();
printf(", версия: %4X, изменения: %4Xn",
(short)ver, (short)(ver >> 16));
printf("Размер свободной расширенной памяти, доступной через XMM:"
" %ld Кбайтn", (long)XMM_QueryLargestFree());
printf("Общий размер расширенной памяти, доступной через XMM:"
" t %ld Кбайтn", (long)XMM_QueryTotalFree());
}
else printf("nДрайвер HIMEM.SYS не установлен.");
printf("n------------------------ Уровень EMS/VCPI ----------------------n");
// Проверяем наличие драйвера EMS/VCPI
if(ems_init()) printf("Драйвер EMS/VCPI не загруженn");
else {
printf("Драйвер EMS/VCPI загружен, ");
// Выводим номер версии драйвера
if((err = ems_ver(&verems)) != 0) {
printf("nОшибка %02.2X при определении версии EMM", err);
exit(-1);
}
printf("версия EMM: %02.2X", verems);
// Определяем присутствие VCPI и его версию
if(vcpi_ver(&ver_hi, &ver_lo) != 0) {
printf("nДрайвер EMM не поддерживает VCPIn");
exit(-1);
}
printf("nВерсия VCPI: %02.2X.%02.2Xn", ver_hi, ver_lo);
}
printf("n------------------------ Уровень DPMI --------------------------");
// Проверяем доступность и параметры сервера DPMI
regs.x.ax = 0x1687;
int86x(0x2F, ®s, ®s, &segregs);
if(regs.x.ax != 0) {
printf("nСервер DPMI не активен"); exit(-1);
}
// Определяем версию сервера DPMI
printf("nВерсия сервера DPMI: ttt%d.%dn",
regs.h.dh, regs.h.dl);
// Определяем тип процессора
printf("Тип процессора:tttt");
if(regs.h.cl == 2) printf("80286");
else if(regs.h.cl == 3) printf("80386");
else if(regs.h.cl == 4) printf("80486");
// Определяем возможность работы с 32-разрядными
// программами
dpmi_flags = regs.x.bx;
printf("nПоддержка 32-разрядных программ:t");
if(dpmi_flags && 1) printf("ПРИСУТСТВУЕТ");
else printf("ОТСУТСТВУЕТ");
// Определяем размер области памяти для сервера DPMI
hostdata_size = regs.x.si;
printf("nРазмер памяти для сервера DPMI:tt%d байт",
hostdata_size * 16);
// Определяем адрес точки входа в защищённый режим
FP_SEG(pm_entry) = segregs.es;
FP_OFF(pm_entry) = regs.x.di;
printf("nАдрес точки входа в защищённый режим: t%Fpn",
pm_entry);
getch();
}
/**
*.Name ems_init
*.Title Функция проверяет установку драйвера EMS
*
*.Descr Эта функция проверяет наличие драйвера EMS
*
*.Proto int ems_init(void);
*
*.Params Не используются
*
*.Return 0 - драйвер EMS установлен;
* 1 - драйвер EMS не установлен.
*
*.Sample ems_test.c
**/
int ems_init(void) {
void (_interrupt _far *EMS_driver_adr)(void);
char _far *EMS_driver_name;
char test_name[8];
int i;
EMS_driver_adr = _dos_getvect(0x67);
FP_SEG(EMS_driver_name) = FP_SEG (EMS_driver_adr);
FP_OFF(EMS_driver_name) = 10;
for(i=0; i<8; i++) test_name[i] = EMS_driver_name[i];
if(strncmp(test_name, "EMMXXXX0", 8) == 0) return(0);
else return(1);
}
/**
*.Name ems_ver
*.Title Определение версии драйвера EMS
*
*.Descr Эта функция возвращает номер версии
* драйвера EMS в двоично-десятичном формате.
*
*.Proto int ems_ver(char *ver);
*
*.Params char *ver - указатель на байт, в который
* будет записан номер версии.
*
*.Return Номер версии драйвера EMS в формате BCD
*
*.Sample ems_test.c
**/
int ems_ver(char *ver) {
union REGS reg;
reg.x.ax = 0x4600;
int86(0x67, ®, ®);
*ver = reg.h.al;
return(reg.h.ah);
}
int vcpi_ver(char *ver_hi, char *ver_lo) {
union REGS reg;
reg.x.ax = 0xDE00;
int86(0x67, ®, ®);
*ver_hi = reg.h.bh;
*ver_lo = reg.h.bl;
return(reg.h.ah);
}
Исходные тексты функций, вызываемых утилитой MEMOSCOP приведены
ниже:
*MEMOSCOP*, © Frolov A.V., 1992
--------------------------------------------------------
Файл cpu.asm
DEAL
MODEL SMALL
P386
PUBLIC _getcpu
CODESEG
PROC _getcpu NEAR
; --------------------------------------------------------
; Пытаемся установить старшую тетраду регистра флагов в 0.
; Если программа работает на процессоре 8086, в этом
; байте все биты будут установлены в 1
; --------------------------------------------------------
mov dx, 86 ; Тип процессора - 8086
pushf
pop bx
and bh,0Fh
push bx
popf
pushf
pop ax
and ah,0F0h
cmp ah,0F0h
je cpu_end
; --------------------------------------------------------
; Для теста на процессор 80286 пытаемся установить старшую
; тетраду регистра флагов в единицы. Если программа
; выполняется на процессоре 80286, эти биты останутся
; равными нулю
; --------------------------------------------------------
mov dx, 286 ; Тип процессора - 80286
or bh,0F0h
push bx
popf
pushf
pop ax
test ah,0F0h
jz cpu_end
; --------------------------------------------------------
; Для того, чтобы отличить процессор 80386 от процессора
; 80486, используем бит 18 регистра флагов EFLAGS.
; Если программа может изменить состояние этого бита,
; она выполняется на процессоре 80486. В противном случае
; используется процессор 80386.
; --------------------------------------------------------
; Созраняем указатель стека
mov edx,esp
; Выравниваем указатель стека для предотвращения
; исключения при установке флага AC
and esp,not 3
; Копируем регистр EFLAGS в регистр EAX
pushfd
pop eax
; Сохраняем начальное значение регистра EFLAGS
mov ecx,eax
; Переключаем флаг AC
xor eax,40000H
; Пытаемся записать измененное значение обратно в регистр
; EFLAGS
push eax
popfd
; Копируем регистр EFLAGS в регисрр EAX
pushfd
pop eax
; Сравниваем старое и новое значения бита AC
xor eax,ecx
shr eax,18
and eax,1
push ecx
; Восстанавливаем регситр EFLAGS
popfd
; Восстанавливаем указатель стека
mov esp,edx
; Теперь если программа выполняется на процессоре 80386,
; регистр AX содержит значение 0, а если на процессоре
; 80486 - значение 1.
mov dx,386 ; Тип процессора - 80386
test ax,ax
jz cpu_end
mov ax,486 ; Тип процессора - 80486
cpu_end:
mov ax, dx
ret
ENDP _getcpu
END
Для работы с функциями драйвера HIMEM.SYS используется интерфейс,
описанный нами в томе 2 "Библиотеки системного программсита":
*MEMOSCOP*, © Frolov A.V., 1992
--------------------------------------------------------
Файл xmmc.asm
; Это интерфейсный модуль для вызова функций
; XMS из Си. Текст программы рассчитан на
; модель памяти Small.
.model small,c
.DATA
; В этом месте будет храниться адрес
; управляющей функции XMM
XMM_Control dd ?
.CODE
; Макроопределения для выполнения соглашения об
; использовании регистров в процедурах Си
c_begin macro
push bp
mov bp,sp
push si
push di
endm
c_end macro
pop di
pop si
mov sp,bp
pop bp
ret
endm
; Все процедуры должны быть public
public XMM_Installed
public XMM_Version
public XMM_RequestHMA
public XMM_ReleaseHMA
public XMM_GlobalEnableA20
public XMM_GlobalDisableA20
public XMM_EnableA20
public XMM_DisableA20
public XMM_QueryA20
public XMM_QueryLargestFree
public XMM_QueryTotalFree
public XMM_AllocateExtended
public XMM_FreeExtended
public XMM_MoveExtended
public XMM_LockExtended
public XMM_UnLockExtended
public XMM_GetHandleLength
public XMM_GetHandleInfo
public XMM_ReallocateExtended
public XMM_RequestUMB
public XMM_ReleaseUMB
;**
;.Name XMM_Installed
;.Title Получение адреса управляющей функции
;
;.Descr Эта функция проверяет наличие драйвера
; HIMEM.SYS и в случае его присуствия
; запоминает адрес управляющей функции.
;
;.Proto unsigned XMM_Installed(void);
;
;.Params Не используются
;
;.Return 0 - драйвер HIMEM.SYS не установлен;
; 1 - драйвер HIMEM.SYS установлен.
;
;.Sample xms_test.c
;**
XMM_Installed proc near
c_begin
mov ax, 4300h
int 2fh
cmp al, 80h
jne NotInstalled
mov ax, 4310h
int 2fh
mov word ptr [XMM_Control], bx
mov word ptr [XMM_Control+2], es
mov ax,1
jmp Installed
NotInstalled:
mov ax, 0
Installed:
c_end
XMM_Installed endp
;**
;.Name XMM_Version
;.Title Определение версии драйвера HIMEM.SYS
;
;.Descr Эта функция определяет версию драйвера
; HIMEM.SYS
;
;.Proto long XMM_Version(void);
;
;.Params Не используются
;
;.Return Номер версии в младших 16 битах,
; номер изменений - в старших 16 битах
; возвращаемого значения
;
;.Sample xms_test.c
;**
XMM_Version proc near
push si
push di
xor ah,ah
call [XMM_Control]
mov dx, bx
pop di
pop si
ret
XMM_Version endp
;**
;.Name XMM_RequestHMA
;.Title Запросить область HMA
;
;.Descr Эта функция пытается зарезервировать для
; программы область HMA
;
;.Proto long XMM_RequestHMA(unsigned space);
;
;.Params space - размер требуемой области для
; TSR-программы или драйвера,
; 0xffff для прикладной программы;
;
;.Return < 0 - область HMA не назначена программе,
; код ошибки находится в старшем байте.
; 0L - область HMA назначена программе.
;
;.Sample xms_test.c
;**
XMM_RequestHMA proc near
c_begin
mov ah, 1
mov dx, [bp+4]
call [XMM_Control]
xor dx, dx
dec ax
jz @success
mov dh, bl
@success:
c_end
XMM_RequestHMA endp
;**
;.Name XMM_ReleaseHMA
;.Title Освободить область HMA
;
;.Descr Эта функция пытается освободить
; область HMA
;
;.Proto long XMM_ReleaseHMA(void);
;
;.Params Не используются
;
;.Return < 0 - область HMA не освобождена,
; код ошибки находится в старшем байте.
; 0L - область HMA освобождена.
;
;.Sample xms_test.c
;**
XMM_ReleaseHMA proc near
c_begin
mov ah, 2
call [XMM_Control]
xor dx, dx
dec ax
jz @success1
mov dh, bl
@success1:
c_end
XMM_ReleaseHMA endp
;**
;.Name XMM_GlobalEnableA20
;.Title Глобальное разрешение линии A20
;
;.Descr Эта функция разрешает программе, получившей
; доступ к области HMA использовать линию A20
;
;.Proto long XMM_GlobalEnableA20(void);
;
;.Params Не используются
;
;.Return < 0 - линия A20 не включена,
; код ошибки находится в старшем байте.
; 0L - линия A20 включена.
;
;.Sample xms_test.c
;**
XMM_GlobalEnableA20 proc near
c_begin
mov ah, 3
call [XMM_Control]
xor dx, dx
dec ax
jz @success2
mov dh, bl
@success2:
c_end
XMM_GlobalEnableA20 endp
;**
;.Name XMM_GlobalDisableA20
;.Title Глобальное запрещение линии A20
;
;.Descr Эта функция запрещает программе, получившей
; доступ к области HMA использовать линию A20
;
;.Proto long XMM_GlobalDisableA20(void);
;
;.Params Не используются
;
;.Return < 0 - линия A20 не выключена,
; код ошибки находится в старшем байте.
; 0L - линия A20 выключена.
;
;.Sample xms_test.c
;**
XMM_GlobalDisableA20 proc near
c_begin
mov ah, 4
call [XMM_Control]
xor dx, dx
dec ax
jz @success3
mov dh, bl
@success3:
c_end
XMM_GlobalDisableA20 endp
;**
;.Name XMM_EnableA20
;.Title Локальное разрешение линии A20
;
;.Descr Эта функция разрешает программе управлять
; областью расширенной памяти.
;
;.Proto long XMM_EnableA20(void);
;
;.Params Не используются
;
;.Return < 0 - линия A20 не включена,
; код ошибки находится в старшем байте.
; 0L - линия A20 включена.
;
;.Sample xms_test.c
;**
XMM_EnableA20 proc near
c_begin
mov ah, 5
call [XMM_Control]
xor dx, dx
dec ax
jz @success4
mov dh, bl
@success4:
c_end
XMM_EnableA20 endp
;**
;.Name XMM_DisableA20
;.Title Локальное запрещение линии A20
;
;.Descr Эта функция запрещает программе управлять
; областью расширенной памяти.
;
;.Proto long XMM_DisableA20(void);
;
;.Params Не используются
;
;.Return < 0 - линия A20 не выключена,
; код ошибки находится в старшем байте.
; 0L - линия A20 выключена.
;
;.Sample xms_test.c
;**
XMM_DisableA20 proc near
c_begin
mov ah, 6
call [XMM_Control]
xor dx, dx
dec ax
jz @success5
mov dh, bl
@success5:
c_end
XMM_DisableA20 endp
;**
;.Name XMM_QueryA20
;.Title Проверить состояние линии A20
;
;.Descr Эта функция проверяет доступность
; линии A20
;
;.Proto long XMM_QueryA20(void);
;
;.Params Не используются
;
;.Return < 0 - ошибка,
; код ошибки находится в старшем байте.
; 0L - линия A20 выключена,
; 1L - линия A20 включена.
;
;.Sample xms_test.c
;**
XMM_QueryA20 proc near
c_begin
mov ah, 7
call [XMM_Control]
xor dx, dx
or ax, ax
jnz @success6
mov dh, bl
@success6:
c_end
XMM_QueryA20 endp
;**
;.Name XMM_QueryLargestFree
;.Title Определить максимальный размер блока
;
;.Descr Эта функция возвращает размер максимального
; непрерывного блока расширенной памяти,
; который доступен программе.
;
;.Proto long XMM_QueryLargestFree(void);
;
;.Params Не используются
;
;.Return < 0 - ошибка,
; код ошибки находится в старшем байте.
; >= 0 - размер блока.
;
;.Sample xms_test.c
;**
XMM_QueryLargestFree proc near
c_begin
mov ah, 8
call [XMM_Control]
xor dx, dx
or ax, ax
jnz @success7
mov dh, bl
@success7:
c_end
XMM_QueryLargestFree endp
;**
;.Name XMM_QueryTotalFree
;.Title Определить размер расширенной памяти
;
;.Descr Эта функция возвращает размер
; всей имеющейся расширенной памяти.
;
;.Proto long XMM_QueryTotalFree(void);
;
;.Params Не используются
;
;.Return < 0 - ошибка,
; код ошибки находится в старшем байте.
; >= 0 - размер расширенной памяти.
;
;.Sample xms_test.c
;**
XMM_QueryTotalFree proc near
c_begin
mov ah, 8
call [XMM_Control]
or ax, ax
mov ax, dx
mov dx, 0
jnz @success8
mov dh, bl
@success8:
c_end
XMM_QueryTotalFree endp
;**
;.Name XMM_AllocateExtended
;.Title Запросить блок расширенной памяти
;
;.Descr Эта функция выделяет программе блок
; расширенной памяти, в случае успеха
; возвращает индекс полученного блока.
;
;.Proto long XMM_AllocateExtended(unsigned space);
;
;.Params space - размер требуемого блока памяти
; в килобайтах;
;
;.Return < 0 - блок не распределен,
; код ошибки находится в старшем байте.
; > 0L - младший байт содержит индекс
; полученного блока памяти.
;
;.Sample xms_test.c
;**
XMM_AllocateExtended proc near
c_begin
mov ah, 9
mov dx, [bp+4]
call [XMM_Control]
or ax, ax
mov ax, dx
mov dx, 0
jnz @success9
mov dh, bl
@success9:
c_end
XMM_AllocateExtended endp
;**
;.Name XMM_FreeExtended
;.Title Освободить блок расширенной памяти
;
;.Descr Эта функция освобождает блок
; расширенной памяти, полученный функцией
; XMM_AllocateExtended().
;
;.Proto long XMM_FreeExtended(unsigned handle);
;
;.Params handle - индекс освобождаемого блока памяти;
;
;.Return < 0 - блок не распределен,
; код ошибки находится в старшем байте.
; 0L - блок освобожден.
;
;.Sample xms_test.c
;**
XMM_FreeExtended proc near
c_begin
mov ah, 0Ah
mov dx, [bp+4]
call [XMM_Control]
xor dx, dx
dec ax
jz @successA
mov dh, bl
@successA:
c_end
XMM_FreeExtended endp
;**
;.Name XMM_MoveExtended
;.Title Копировать блок расширенной памяти
;
;.Descr Эта функция копирует блок
; расширенной памяти, используя структуру
; struct XMM_Move:
;
; struct XMM_Move {
; unsigned long Length;
; unsigned short SourceHandle;
; unsigned long SourceOffset;
; unsigned short DestHandle;
; unsigned long DestOffset;
; };
;
;.Proto long XMM_MoveExtended(struct
; XMM_Move *move_descr);
;
;.Params struct XMM_Move *move_descr -
; указатель на структуру, описывающую
; что, откуда и куда надо копировать.
;
;.Return < 0 - ошибка при копировании,
; код ошибки находится в старшем байте.
; 0L - блок скопирован успешно.
;
;.Sample xms_test.c
;**
XMM_MoveExtended proc near
c_begin
mov ah, 0Bh
mov si, [bp+4];
call [XMM_Control]
xor dx, dx
dec ax
jz @successB
mov dh, bl
@successB:
c_end
XMM_MoveExtended endp
;**
;.Name XMM_LockExtended
;.Title Заблокировать блок расширенной памяти
;
;.Descr Эта функция блокирует блок расширенной
; памяти и возвращает 31 разряд его
; физического адреса.
;
;.Proto long XMM_LockExtended(unsigned handle);
;
;.Params handle - индекс блокируемого блока памяти;
;
;.Return < 0 - блок не заблокирован,
; код ошибки находится в старшем байте.
; > 0L - блок заблокирован, функция
; возвращает физический адрес блока
; памяти.
;
;.Sample xms_test.c
;**
XMM_LockExtended proc near
c_begin
mov ah, 0Ch
mov dx, [bp+4]
call [XMM_Control]
xchg ax, bx
dec bx
jz XMML_Success
mov dh, al
XMML_Success:
c_end
XMM_LockExtended endp
;**
;.Name XMM_UnLockExtended
;.Title Разблокировать блок расширенной памяти
;
;.Descr Эта функция разблокирует блок расширенной
; памяти.
;
;.Proto long XMM_UnLockExtended(unsigned handle);
;
;.Params handle - индекс блока памяти;
;
;.Return < 0 - блок не разблокирован,
; код ошибки находится в старшем байте.
; 0L - блок разблокирован.
;
;.Sample xms_test.c
;**
XMM_UnLockExtended proc near
c_begin
mov ah, 0Dh
mov dx, [bp+4]
call [XMM_Control]
xor dx, dx
dec ax
jz @successC
mov dh, bl
@successC:
c_end
XMM_UnLockExtended endp
;**
;.Name XMM_GetHandleLength
;.Title Получить длину блока расширенной памяти
;
;.Descr Эта функция возвращает длину блока
; расширенной памяти по его индексу.
;
;.Proto long XMM_GetHandleLength(unsigned handle);
;
;.Params handle - индекс блока памяти;
;
;.Return < 0 - произошла ошибка,
; код ошибки находится в старшем байте.
; > 0L - длина блока в килобайтах.
;
;.Sample xms_test.c
;**
XMM_GetHandleLength proc near
c_begin
mov ah, 0Eh
mov dx, [bp+4]
call [XMM_Control]
or ax, ax
mov ax, dx
mov dx, 0
jnz @successD
mov dh, bl
@successD:
c_end
XMM_GetHandleLength endp
;**
;.Name XMM_GetHandleInfo
;.Title Получить информацию о блоке расширенной памяти
;
;.Descr Эта функция возвращает общее
; количество индексов в системе и
; содержимое счетчика блокирования для
; заданного индекса.
;
;.Proto long XMM_GetHandleInfo(unsigned handle);
;
;.Params handle - индекс блока памяти;
;
;.Return < 0 - произошла ошибка,
; код ошибки находится в старшем байте.
; > 0L - младший байт - общее количество
; индексов в системе;
; старший байт - счетчик блокирования.
;
;.Sample xms_test.c
;**
XMM_GetHandleInfo proc near
c_begin
mov ah, 0Eh
mov dx, [bp+4]
call [XMM_Control]
mov dx, bx
or ax, ax
mov ax, dx
mov dx, 0
jnz @successE
mov dh, bl
@successE:
c_end
XMM_GetHandleInfo endp
;**
;.Name XMM_ReallocateExtended
;.Title Изменить размер блока расширенной памяти
;
;.Descr Эта функция изменяет размер выделенного
; блока расширенной памяти.
;
;.Proto long XMM_ReallocateExtended(unsigned handle,
; unsigned new_size);
;
;.Params handle - индекс блока памяти;
; new_size - новый размер блока памяти
; в килобайтах;
;
;.Return < 0 - блок не распределен,
; код ошибки находится в старшем байте.
; > 0L - младший байт содержит индекс
; полученного блока памяти.
;
;.Sample xms_test.c
;**
XMM_ReallocateExtended proc near
c_begin
mov ah, 0Fh
mov dx, [bp+4]
mov bx, [bp+6]
call [XMM_Control]
xor dx, dx
dec ax
jz @successF
mov dh, bl
@successF:
c_end
XMM_ReallocateExtended endp
;**
;.Name XMM_RequestUMB
;.Title Запросить область UMB
;
;.Descr Эта функция пытается зарезервировать для
; программы область UMB
;
;.Proto long XMM_RequestUMB(unsigned space);
;
;.Params space - размер требуемой области
; в параграфах;
;
;.Return < 0 - область UMB не назначена программе,
; код ошибки находится в старшем байте;
; максимальный размер доступного блока
; в младшем слове (16 разрядов);
; > 0L - область UMB назначена программе,
; младшее слово содержит сегмент блока
; UMB, старший - размер выделенного
; блока UMB.
;
;.Sample xms_test.c
;**
XMM_RequestUMB proc near
c_begin
mov ah, 10h
mov dx, [bp+4]
call [XMM_Control]
xchg bx, ax
dec bx
jz RUMB_Success
xchg ax, dx
mov dh, dl
RUMB_Success:
c_end
XMM_RequestUMB endp
;**
;.Name XMM_ReleaseUMB
;.Title Освободить область UMB
;
;.Descr Эта функция пытается освободить
; область UMB
;
;.Proto long XMM_ReleaseUMB(unsigned segment);
;
;.Params segment - сегмент освобождаемого блока UMB*
;
;.Return < 0 - область UMB не освобождена,
; код ошибки находится в старшем байте.
; 0L - область UMB освобождена.
;
;.Sample xms_test.c
;**
XMM_ReleaseUMB proc near
c_begin
mov ah, 11h
mov dx, [bp+4]
call [XMM_Control]
xor dx, dx
dec ax
jz @success10
mov dh, bl
@success10:
c_end
XMM_ReleaseUMB endp
END
Если вы разрабатываете программное обеспечение, защищённое от
несанкционированного копирования, вам необходимо позаботиться
о том, чтобы потенциальные взломщики ("кракеры" и "хакеры")
не смогли выполнить программу инсталляции под управлением отладчика.
Если взломщик сможет "подглядеть" за работой вашей программы,
он рано или поздно разгадает ваш замысел и сведёт на нет все ваши
усилия по защите программы от копирования.
В третьей книге первого тома "Библиотеки системного программиста"
мы излагали некоторые соображения по организации защиты программ
от несанкционированного копирования. Мы, в частности, рассказали
о некоторых методах защиты программ от отладки - использование
таймера, внутренней очереди команд процессора и другие.
Защищённый режим работы процессора открывает перед вами новую
возможность. Возьмите любую программу, приведённую в этой книге
и попытайтесь запустить её под управлением какого-либо отладчика
(например, попробуйте Turbo Debugger или Code View). Всё будет
хорошо до тех пор, пока ваша программа не попытается загрузить
регистр IDTR при помощи команды LIDT. После выполнения этой команды
отладчик зависает и единственное средство вновь оживить компьютер
- нажать на кнопку сброса, расположенную на системном блоке.
Причина очевидна - изменились расположение и формат дескрипторной
таблицы прерываний. Она подготовлена для работы в защищённом режиме,
но отладчик работает в реальном режиме. Поэтому обработка всех
прерываний, в том числе и от клавиатуры, невозможна.
Идея использования защищённого режима работы процессора при создании
программ, защищённых от несанкционированного копирования, очевидна.
Используя примеры программ, приведённые в книге, вы сможете во
время работы программы инсталляции перевести процессор в защищённый
режим и выполнить часть работы по инсталляции в защищённом режиме.
Например, перед переключением в защищённый режим вы можете подготовить
в памяти массив контрольной информации. Расшифровка и проверка
этого массива, а также запись данных в нестандартные сектора инсталляционной
дискеты могут выполняться в защищённом режиме. При этом, пользуясь
обычными отладчиками, невозможно определить действия, выполняемые
в защищённом режиме. Особенно, если участок программы, работающий
в защищённом режиме, зашифрован.
Далее процессор можно вернуть в реальный режим и продолжить процесс
инсталляции.
Находясь в защищённом режиме, вы можете читать и писать сектора
дискеты только используя уровень портов ввода/вывода контроллера
флоппи-диска. Программирование контроллера флоппи-диска описано
в третьей книге первого тома "Библиотеки системного программиста".
Обрабатывать прерывания в защищённом режиме вы уже умеете.
Очевидный недостаток применения защищённого режима при организации
защиты от копирования заключается в необходимости использования
процессоров i80286, i80386 или i80486. Это означает, что указанный
метод непригоден для компьютеров IBM PC/XT, использующих процессор
i8086 или i8088.
Однако в последнее время большинство программных комплексов требует
наличия в компьютере по крайней мере процессора i80286, поэтому
указанным недостатком можно пренебречь. К тому же следует учесть
резкое увеличение стойкости инсталлятора, работающего в защищённом
режиме, к попыткам взлома.
|