Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка2_дрВ.doc
Скачиваний:
1
Добавлен:
19.08.2019
Размер:
851.97 Кб
Скачать

Підтримка мов

Для завантаження нової мови використовується функція LoadKeyboardLayout. Перший аргумент цієї функції вказує на ідентифікатор мови (лінійка з 16-ковим кодом), наприклад: англійська мова має код (0х0409), де молодше слово вказує на ідентифікатор мови, а старше – на дескриптор пристрою, драйвер підтримки мови. В функцію записується символьне значення мови "00000409". Другий аргумент також визначає додатковий прапор, який може приймати одне із значень в табл.3.21.

Значення прапора

Опис

KLF_REORDER

KLF_REPLACELANG

KLF_ACTIVATE

Пересуває розкладку даної мови на перше місце в списку

Якщо вказаний ідентифікатор співпадає з уже заданим раніше, то він все одно заміняє останній

Робить активною завантажену розкладку мови

Таблиця 3.21. Значення прапора для розкладки клавіатури

При успішному виконанні функція поверне ідентифікатор (тип HKL) завантаженої мови. Для вивантаження любої мови використовується функція UnloadKeyboardLayout, яка повертає ненульове значення при успішному виконанні. Функція застосовується для вибору текучої мови. Перший її аргумент вказує на ідентифікатор мови і може приймати одне з двох значень: HKL_NEXT – вибрати наступну мову із списку і HKL_PREV – вибрати попередню мову із списку. Другий аргумент вказує на прапор мови і приймає значення: KLF_REORDER (описаний в табл.3.21), KLF_RESET – скинути блокування використання великих літер (тільки для Windows 2000), KLF_SHIFTLOCK - включити блокування використання великих літер (тільки для Windows 2000). Для отримання інформації використовуються також функції: GetKeyboardLayout i GetKeyboardLayoutName, перша з яких повертає числовий ідентифікатор (тип HKL) текучої мови, а друга – лінійку з іменем розкладки ("00000409"). Реалізація даних функцій наведена в проміжному фрагменті програми нижче.

Покроковий хід роботи для написання макета програми на мові С++ для використання мов кирилиці.

  1. Завантажуємо російську розкладку клавіатури:

  2. Для реалізації російської розкладки клавіатури надаємо змінній hRus системного типу HKL значення, яке визначається функцією LoadKeyboardLayout з параметрами: "00000409"- код мови і макросом KLF_REORDER для перезамовлення російської кирилиці.

  3. Робимо російську мову текучою:

  4. Порівнюємо, якщо змінна hRus не NULL, то:

  5. Робимо активною російську мову за допомогою системної функції ActivateKeyboardLayout задаючи в ній параметр мови – hRus і параметр KLF_REORDER – макрос для реалізації активності.

  6. При закінченні роботи з російською клавіатурою, вивантажуємо її:

  7. Використовуємо системну функцію UnloadKeyboardLayout з параметром мови кирилиці – в даному випадку hRus.

Варіанти практичних завдань: Написати фрагменти програм на мові С++.

Варіант

Програма 1

Програма 2

1

функція для встановлення часу затримки 250 мс.

функція для використання комбінації «гарячих» клавіш «Ctrl+Alt+F1».

2

функція для встановлення часу затримки 500 мс.

функція для використання комбінації «гарячих» клавіш «Ctrl+Shift+F1».

3

функція для встановлення часу затримки 750мс.

функція для використання комбінації «гарячих» клавіш «Ctrl+Home».

4

функція для встановлення часу затримки 1000 мс.

функція для використання комбінації «гарячих» клавіш «Ctrl+Alt+F2».

5

функція для встановлення частоти автоповтору 11 сим/с

функція для використання комбінації «гарячих» клавіш «Shift+Alt+F1».

6

функція для встановлення частоти автоповтору 13 сим/с

функція для використання комбінації «гарячих» клавіш «Ctrl+NumLock+F3».

7

функція для встановлення частоти автоповтору 31 сим/с

функція для використання комбінації «гарячих» клавіш «Ctrl+Shift+F11».

8

функція для встановлення частоти автоповтору 25 сим/с

функція для використання комбінації «гарячих» клавіш «Ctrl+Alt+F4».

9

функція для встановлення частоти автоповтору 22 сим/с

функція для використання комбінації «гарячих» клавіш «Ctrl+Alt+F7».

10

функція для встановлення частоти автоповтору 18 сим/с

функція для використання комбінації «гарячих» клавіш «Alt+Delete».

11

функція для встановлення частоти автоповтору 23 сим/с

функція для використання комбінації «гарячих» клавіш «Ctrl+Shift+F6».

12

функція для встановлення частоти автоповтору 29 сим/с

функція для використання комбінації «гарячих» клавіш «Ctrl+Alt+F8».

13

функція для встановлення частоти автоповтору 28 сим/с

функція для використання комбінації «гарячих» клавіш «Shift+F12».

14

функція для встановлення частоти автоповтору 19 сим/с

функція для використання комбінації «гарячих» клавіш «Shift+Insert».

15

функція для встановлення частоти автоповтору 12 сим/с

функція для використання комбінації «гарячих» клавіш «Alt+Insert».

16

функція для встановлення частоти автоповтору 16 сим/с

функція для використання комбінації «гарячих» клавіш «Ctrl+Alt+F9».

17

функція для встановлення частоти автоповтору 24 сим/с

функція для використання комбінації «гарячих» клавіш «Ctrl+Shift+Tab».

18

функція для встановлення частоти автоповтору 27 сим/с

функція для використання комбінації «гарячих» клавіш «Shift+Scroll Lock».

19

функція для встановлення частоти автоповтору 20 сим/с

функція для використання комбінації «гарячих» клавіш «Ctrl+Alt+F11».

20

функція для встановлення частоти мигання курсора 300 мс

функція для використання комбінації «гарячих» клавіш «Alt+Pause».

21

функція для встановлення частоти мигання курсора 400 мс

функція для використання комбінації «гарячих» клавіш «Shift+Esc».

22

функція для встановлення частоти мигання курсора 500 мс

функція для використання комбінації «гарячих» клавіш «Ctrl+Alt+F10».

23

функція для встановлення частоти мигання курсора 600 мс

функція для використання комбінації «гарячих» клавіш «Alt+Insert+Tab».

24

функція для встановлення частоти мигання курсора 700 мс

функція для використання комбінації «гарячих» клавіш «Ctrl+Shift+F7».

25

функція для встановлення частоти мигання курсора 800 мс

функція для використання комбінації «гарячих» клавіш «Alt+Esc».

26

функція для встановлення частоти мигання курсора 900 мс

функція для використання комбінації «гарячих» клавіш «Ctrl+Alt+F12».

27

функція для встановлення частоти мигання курсора 1000 мс

функція для використання комбінації «гарячих» клавіш «Shift+Pause».

28

функція для встановлення частоти мигання курсора 1100 мс

функція для використання комбінації «гарячих» клавіш «Alt+PageUp».

29

функція для встановлення англійської кирилиці.

функція для використання комбінації «гарячих» клавіш «Alt+NumLock».

30

функція для встановлення російської кирилиці.

функція для використання комбінації «гарячих» клавіш «Shift+Insert».

Практичне завдання 7

Тема: Використання функцій вводу-виводу

Мета: ознайомитися з функціями, призначеними для написання программ для зовнішніх пристроїв вводу-виводу, вміти використовувати їх при написанні программ для зовнішніх пристроїв.

Теоретичні відомості.

Існує 6 функцій для роботи з портами: три із них використовуються для виводу, а три – для вводу. До функцій читання відносяться:

  • _inp – дозволяє зчитати один байт із вказаного порту;

  • _inpw – дозволяє зчитати одне слово з указаного порту;

  • _inpd – дозволяє прочитати подвійне слово з указаного порту.

У всіх цих функціях потрібно вказати номер порту зчитування. Максимальне значення адреси порту обмежене числом 65535, якого зовсім достатньо для роботи зі всіма існуючими в системі значеннями.

Покроковий хід роботи для написання макета програми використання функцій читання даних із порту:

  1. Підключаємо необхідний файл ресурсів <conio.h> зі стандартної бібліотеки.

  2. Прочитаємо значення базової пам’яті в кілобайтах, використовуючи бібліотечну системну функцію GetBaseMemory.Результат повернення типу іnt. Функція задається без параметрів.

  3. Об’являємо змінні lowBase і highBase типу BYTE для отримання старшого і молодшого байтів, пока обнулюємо їх.

  4. Читаємо інформацію з CMOS-пам’яті: Функцією _outp бібліотечного класу для роботи з портами (0x70) записуємо номер першого регістра 0x15.

  5. Читаємо молодший біт командою _inp (0x71) і присвоюємо його значення змінній lowBase.

  6. Функцією _outp бібліотечного класу для роботи з портами (0x70) записуємо номер другого регістра 0x16.

  7. Читаємо старший біт командою _inp (0x71) і присвоюємо його значення змінній highBase.

  8. Повертаємо розмір базової пам’яті в кілобайтах командою return і побітовим додаванням highBase << 8 та lowBase змінних.

Покроковий хід роботи для написання макету програми управління клавіатурою:

  1. Функція KeyBoardOnOff для управління клавіатурою без значення повернення (типу void) з логічним параметром bOff.

  2. Об’являємо змінну state для текучого стану типу BYTE.

  3. Якщо bOff не нуль то виключаємо клавіатуру.

  4. Застосовуючи функціючитання з системного класу _inp з адресою 0x61, отримуємо текучий стан state.

  5. Встановлюємо біт 7 в 1 командою state |=0x80.

  6. Командою _outp записуємо обновлене значення змінної state в порт 0x61

Функції запису в порт мають два аргументи: перший вказує номер порту, другий – для зберігання даних передачі. До них відносяться:

  • _outp – дозволяє записати один байт в указаний порт;

  • _outpw - дозволяє записати слово в указаний порт;

  • _outpd - дозволяє записати подвійне слово в указаний порт.

Максимальне значення адреси порту також обмежене числом 65535. Приклад застосування цих функцій:

Покроковий хід роботи для написання макету програми використання функцій, які в комплексі використовуються для запису даних в порт:

  1. Напишемо функцію ResetDrive типу bool для скидання даних пристрою АТА/АТАРІ.

  2. Використовуємо перший пристрій на другому каналі (як правило CD-ROM).

  3. Командою _outp пишемо команду скидання 08h для даних пристрою 0x177.

  4. Перевіряємо результат виконання:

  5. Організовуємо цикл по змінній 0<i <5000.

  6. Перевіряємо командою _inp біт 7- BUSY! Тобто ((0x177) & 0x80) = 0x00.

  7. Команда успішно завершена, повертаємо значення true.

  8. Повертаємо значення false.

  1. Напишемо функцію Eject з логічним поверненням і параметром bOpen типу bool для управління лотком CD-ROM (відкриттям/закриттям).

  2. Ініціалізуємо змінну iTimeWait = 50000 часу затримки типу int.

  3. Створюємо формат пакетної команди для відкриття лотка CD-ROM (6-й елемент массиву Eject [6], створюємо рядок { 0x1B, 0, 2, 0, 0, 0 } типу WORD.

  4. Створюємо формат пакетної команди для закриття лотка CD-ROM (6-й елемент массиву Eject [6], створюємо рядок { 0x1B, 0, 3, 0, 0, 0 } типу WORD).

  5. Перевіряємо готовність пристрою:

  6. Знову організовуємо цикл по змінній затримки iTimeWait.

  7. Читаємо стан порту функціями_inp (0x177) & 0x80 == 0x00 і _inp (0x177) & 0x08 == 0x00. Якщо ці дві умови виконані – виходимо з програми функції.

  8. Якщо iTimeWait < 1 - закінчився час очікування, здійснюємо повернення.

  9. Вибираємо перший пристрій на другому каналі:

  10. Пишемо в порт 0x176 команду пакетної передачі 0xA0 (А0h) функцією _outp.

  11. Перед посилкою пакетної команди потрібно перевірити стан порта:

  12. Ініціалізуємо змінну iTimeWait = 50000 часу затримки типу int.

  13. Очікуємо готовність пристрою:

  14. Знову організовуємо цикл по змінній затримки iTimeWait.

  15. Читаємо стан порту функціями _inp (0x177) & 0x80 == 0x00 і _inp (0x177) & 0x08 == 0x00 і перевіряємо, якщо рівні 0 – виходимо з циклу.

  16. Якщо iTimeWait < 1, закінчився час затримки, поветаємось.

  17. Пишемо в порт 0x177 команду пакетної передачі A0h.

  18. Очікуємо готовність пристрою:

  19. Знову організовуємо цикл по змінній затримки iTimeWait.

  20. Читаємо стан порту функціями _inp (0x177) & 0x80 == 0x00 і _inp (0x177) & 0x08 == 0x01 і перевіряємо, якщо рівні 0 – виходимо з циклу.

  21. Якщо iTimeWait < 1, закінчився час затримки, поветаємось.

  22. Пишемо в порт пакетну команду:

  23. Якщо параметр bOpen не 0, відкрити лоток.

  24. Організовуємо цикл по змінній і від 0 до 6 для запису усіх 6 елементів массиву Eject[i], з яких складається 12-байтова команда.

  25. Записуємо в порт з адресом 0x170 12-байтову команду за допомогою функції _outpw і по додавання елементів масиву Eject[i] (кожен по 2 байти).

  26. Інакше - закрити лоток:

  27. Організовуємо цикл по змінній j від 0 до 6 для формування 12-байтної команди закриття з елементів масиву Close[j].

  28. Записуємо в порт з адресом 0x170 12-байтову команду за допомогою функції _outpw і по додавання елементів масиву Close[j] (кожен по 2 байти).

  29. Перевіряємо результат виконання команди, якщо потрібно:

  30. Ініціалізуємо змінну iTimeWait = 50000 часу затримки типу int.

  31. Очікуємо готовність пристрою:

  32. Знову організовуємо цикл по змінній затримки iTimeWait.

  33. Читаємо стан порту функціями _inp (0x177) & 0x80 == 0x00 і _inp (0x177) & 0x01 == 0x00 і _inp (0x177) & 0x40 == 0x01 перевіряємо, якщо рівні 0 – виходимо з циклу.

  34. Якщо змінна iTimeWait < 1 - закінчився час затримки, повертаємось з функції.

Варіанти практичних завдань. Написати фрагменти програм на мові С++.

Варіант 1

Варіант 2

Варіант 3

Варіант 4

Написати программу функції для читання даних із порту, вживаючи системні бібліотечні функції для читання.

Написати программу функції для управління клавіатурою при читанні/записі.

Написати программу функції для скидання даних пристрою АТА/АТАРІ.

Написати программу функції для управління лотком CD-ROM ізапису даних, вживаючи системні бібліотечні функції для запису.

Практичне завдання 8

Тема: Використання функції DeviceIoControl

Мета: вивчити можливості функції DeviceIoControl, зрозуміти призначення її параметрів та способи їх використання при системному апаратному програмуванні.

Теоретичні відомості.

Універсальність функції полягає в тому, що вона працює з усіма драйверами, які підтримують операцію вводу-виводу. Функція DeviceIoControl має 8 аргументів: 1-й вказує ім’я драйвера, через який здійснюється управління портами; 2-й представляє ідентифікатор коду необхідної операції в даний момент; 3-й і 4-й дозволяють вказати буфер для даних передачі і його розмір, його застосовують для операції запису, в інших випадках надають значення NULL; 5-й і 6-й аргументи служать для отримання даних від пристрою (вказівник на буфер даних і розмір буфера), якщо не використовуються то обертаємо в NULL; 7-й вказує на кількість отриманих даних; 8-й вказує на структуру OVERLAPPED, яка використовується при асинхронному вводі-виводі. Розглянемо приклади з даною функцією для читання сектора з диску.

Покроковий хід роботи для написання програми для читання сектора з диску:

  1. Підключаємо в програму h-файл "stdafx.h", який повинен знаходитись в нашій робочій директорії.

  2. Визначаємо код функції драйвера VWIN32_DIOC_DOS_DRIVEINFO 6 черездирективу процесора С++.

  3. Визначаємо прапор переносу CF_FLAG 1.

  4. Задаємо додаткові структури:

  5. Визначаємо структуру як доступ до класової структури _DIOC_REGISTERS. Початок структури.

  6. Задаємо змінну reg_EBX типу DWORD.

  7. Задаємо змінну reg_EDX типу DWORD.

  8. Задаємо змінну reg_ECX типу DWORD.

  9. Задаємо змінну reg_EAX типу DWORD.

  10. Задаємо змінну reg_EDI типу DWORD.

  11. Задаємо змінну reg_ESI типу DWORD.

  12. Зразок структури DIOC_REGISTERS.Кінець задання структури.

  13. Реалізація дії pack (1) як константи чи макроса через директиву #pragma.

  14. Визначаємо структуру _DATABLOCK як доступ до класової. Початок структури:

  15. Номер початкового сектору dwStartSector типу DWORD.

  16. Кількість секторів wNumSectors типу WORD.

  17. Вказівник на буфер даних pBuffer типу DWORD.

  18. Зразок структури DATABLOCK. Кінець задання структури.

  19. Реалізація дії pack () як константи чи макроса через директиву #pragma.

  20. Пишемо функцію для читання секторів з диска:

  21. Функція ReadSector типу bool з параметрами: uDrive типу unsigned int, dwStartSector типу DWORD, wNumSectors типу WORD і lpBuffer типу LPBYTE.

  22. Назва для файла драйвера hDriver типу HANDLE.

  23. Ініціалізуємо структуру reg={0} типу DIOC_REGISTERS по зразку заданої структури.

  24. Ініціалізуємо структуру data = {0} типу DATABLOCK по зразку другої структури.

  25. Створюємо логічну змінну для результату bResult.

  26. Ініціалізуємо змінну dwResult = 0 типу DWORD.

  27. Ініціалізуємо сам драйвер системним викликом CreateFile з переметрами \\\\.\\vwin32, 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, 0 і присвоюємо файловій змінній hDriver.

  28. Якщо драйвер hDriver недоступний, тобто системний виклик повернув значення INVALID_HANDLE_VALUE – виходимо з функції і повертаємо значення falce.

  29. Заповнюємо структуру даних DATABLOCK:

  30. Елементу dwStartSector структури data присвоюємо значення dwStartSector через операцію з’єднання з елементом структури (крапка).

  31. Елементу wNumtSector структури data присвоюємо значення wNumSector через операцію з’єднання з елементом структури (крапка).

  32. Елементу pBuffer структури data присвоюємо значення lpBuffer типу DWORD через операцію з’єднання з елементом структури (крапка).

  33. Заповнюємо структуру управління reg:

  34. Елементу reg_EAX структури reg присвоюємо значення функції 0x7305 переривання 21h через операцію з’єднання з елементом структури (крапка).

  35. Елементу reg_EВX структури reg присвоюємо структуру data (операція &-взяття адреси початку структури) типу DWORD через операцію з’єднання з елементом структури (крапка).

  36. Елементу reg_EСX структури reg присвоюємо -1 через операцію з’єднання з елементом структури (крапка).

  37. Елементу reg_EDX структури reg присвоюємо значення uDrive - номер логічного диску через операцію з’єднання з елементом структури (крапка).

  38. Викликаємо функцію DeviceIoControl:

  39. Організовуємо зчитування і передачу інформації функцією DeviceIoControl при взаємодії з драйвером з відомими параметрами функції: hDriver, VWIN32_DIOC_DOS_DRIVEINFO, &reg, sizeof (reg), &reg, sizeof (reg), &dwResult, 0; де в параметрах 3-5 організована операція взяття адреси по посиланню на тип даних і присвоюємо результат змінній bResult.

  40. Якщо виникла помилка – виходимо із функції:

  41. Якщо bResult або reg.reg_Flags &CF_FLAG (через посилання на елемент і побітове додавання) пусті величини, то:

  42. Функцією CloseHandle закриваємо драйвер hDriver.

  43. Повертаємо значення false.

  44. Повертаємо значення true.

Покроковий хід роботи для написання макета програми для читання двох секторів диска:

  1. Приклад функції ReadSector для читання двох секторів диска: номер секторів може бути: 0- по замовчуванню, 1- А, 2- В, 3- С, 4-D, 4-E, і т.д.

  2. Виділяємо пам’ять buffer через посилання на тип char* для двох секторів жорсткого диску і пока присвоюємо значення NULL.

  3. Змінній buffer присвоюємо величину зарезервованої командою new пам’яті типу char [512*2] – для 2-х сегментів.

  4. Викликаємо функцію читання секторів ReadSector (3, 0, 2, (LPBYTE) buffer) з номерами секторів та побайтового завантаження в буфер пам’яті.

  5. Звільняємо пам’ять [] buffer командою delete.

Слід відмітити, що в професійних системах (NT, XP i 2000) використовувати функцію DeviceIoControl не потрібно, достатньо тільки відкрити функцією CreateFile логічний диск (навіть CD-ROM) і за допомогою функції ReadFile прочитати дані з диску. Розглянемо приклад запису даних на логічний диск, використовуючи вищезгадані функції.

Покроковий хід роботи для написання макету програми для запису даних на логічний диск:

  1. Функція для запису сектора диску:

  2. Створюється логічна функція WriteSector з параметрами uDrive (сектор) типу unsigned int, dwStartSector (початок сектора) типу DWORD, wNumSectors (кількість секторів) типу WORD і lpBuffer (побайтова операція) типу LPBYTE.

  3. Об’являємо змінну hDriver для драйвера типу HANDLE.

  4. Ініціалізуємо структуру reg = {0} управління регістром системного типу DIOC_REGISTERS

  5. Ініціалізуємо структуру даних data = {0} типу DATABLOCK.

  6. Об’являємо змінну bResult для логічного завершення.

  7. Ініціалізуємо змінну результату проміжного запису dwResult = 0 типу DWORD.

  8. Ініціалізуємо драйвер:

  9. Ініціалізуємо сам драйвер системним викликом CreateFile з переметрами \\\\.\\vwin32, 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, 0 і присвоюємо файловій змінній hDriver.

  10. Якщо драйвер hDriver недоступний, тобто передалося системне значення при відкритті INVALID_HANDLE_VALUE, виходимо з функції і повертаємо значення falce.

  11. Заповнюємо структуру даних DATABLOCK:

  12. Елементу dwStartSector структури data присвоюємо значення dwStartSector через операцію з’єднання з елементом структури (крапка).

  13. Елементу wNumtSector структури data присвоюємо значення wNumSector через операцію з’єднання з елементом структури (крапка).

  14. Елементу pBuffer структури data присвоюємо значення lpBuffer типу DWORD через операцію з’єднання з елементом структури (крапка).

  15. Заповнюємо структуру управління:

  16. Елементу reg_EAX структури reg присвоюємо значення функції 0x7305 переривання 21h через операцію з’єднання з елементом структури (крапка).

  17. Елементу reg_EВX структури reg присвоюємо структуру data (операція &-взяття адреси початку структури) типу DWORD через операцію з’єднання з елементом структури (крапка).

  18. Елементу reg_EСX структури reg присвоюємо -1 через операцію з’єднання з елементом структури (крапка).

  19. Елементу reg_EDX структури reg присвоюємо значення uDrive - номер логічного диску через операцію з’єднання з елементом структури (крапка).

  20. Елементу reg_ESI структури reg присвоюємо значення адреси 0x6001 через операцію з’єднання з елементом структури (крапка).

  21. Викликаємо функцію DeviceIoControl:

  22. Організовуємо зчитування і передачу інформації для запису функцією DeviceIoControl при взаємодії з драйвером з відомими параметрами функції: hDriver, VWIN32_DIOC_DOS_DRIVEINFO, &reg, sizeof (reg), &reg, sizeof (reg), &dwResult, 0; де в параметрах 3-5 організована операція взяття адреси по посиланню на тип даних і присвоюємо результат змінній bResult.

  23. Якщо виникла помилка – виходимо із функції:

  24. Якщо bResult або reg.reg_Flags &CF_FLAG (через посилання на елемент і побітове додавання) пусті величини, то:

  25. Функцією CloseHandle закриваємо драйвер hDriver.

  26. Повертаємо значення false.

  27. Повертаємо значення true.

Варіанти практичних завдань. Написати фрагменти програм на мові С++.

Варіант 1

Варіант 2

Написати програму функціїдля читання сектора з диску

Написати програму функції для запису сектора диску.

Практичне завдання 9

Тема: Використання драйвера

Мета: навчитися використовувати драйвери VxD і SYS для роботи з портами доступу до портів.

Теоретичні відомості.

Існують два драйвери: віртуальний драйвер пристрою VxD (Windows 98/ME) i системний драйвер SYS (Windows NT/2000/XP), які можна знайти в обновленому варіанті на сайтах: http://aspi32.narod.ru i http://hardopen.narod.ru відповідно. Для прикладу використання даних драйверів напишемо два класи. Перший клас (файл IO32.h) написаний нижче для роботи в Windows 98/ME.

Покроковий хід роботи для написання програми класу з використанням драйвера для запису даних в порт:

  1. Клас CIO32 з використанням драйвера для запису даних в порт:

  2. Включаємо O32.h: інтерфейс, який знаходиться в бібліотечному файлі winioctl.h для класу CIO32.

  3. Визначаємо коди функцій для читання і запису:

  4. Створюємо прототип функції CTL_CODE типу IO32_WRITEPORT з параметрами: FILE_DEVICE_UNKNOWN – для знаходження файлу, 1 – для запису в порт, \METHOD_NEITHER, FILE_ANY_ACCESS - розташування).

  5. Створюємо прототип функції CTL_CODE типу IO32_READPORT з параметрами FILE_DEVICE_UNKNOWN – для знаходження файлу, 2– для читання з порту, \METHOD_NEITHER, FILE_ANY_ACCESS - розташування).

  6. Об’являємо клас CIO32

  7. Загальнодоступні конструктори і функції - public:

  8. Конструктор CIO32 ().

  9. Деструктор ~CIO32 ().

  10. Загальні функції:

  11. Ініціалізуємо драйвер функцією InitPort () значення повернення типу bool.

  12. Функція для зчитування значення із порту:

  13. Функція inPort типу bool з параметрами: wPort типу WORD, pdwValue типу PDWORD, bSize типу BYTE для читання в порт.

  14. Функція для запису значення в порт:

  15. Функція outPort типу bool з параметрами: wPort типу WORD, pdwValue типу PDWORD, bSize типу BYTE для читання в порт.

  16. Недоступні функції – private тобто закрита частина класу:

  17. Дескриптор драйвера hVxD типу HANDLE.

  18. Структура управління:

  19. Реалізація дії pack (1) як константи чи макроса через директиву #pragma.

  20. Об’явлення структури tagPort32 типу strukt.

  21. Змінна порта wPort системного типу unsigned short – USHORT.

  22. Змінна значення порта dwValue типу ULONG.

  23. Змінна розміру порта bSize типу UCHAR.

  24. Реалізація дії pack () як константи чи макроса через директиву #pragma.

  25. Закінчення класу.

Покроковий хід роботи для написання програми файлу ІО32.срр:

  1. Включення файла бібліотеки "stdafx.h".

  2. Включення попереднього файла класу"IO32.h".

  3. Реалізація класу CIO32:

  4. Конструктор CIO32 на функцію конструктора класу CIO32 ().

  5. Адресі hVxD надаємо значення NULL і виконуємо функцію класу.

  6. Деструктор CIO32 на функцію деструктора ~CIO32 ().

  7. Якщо hVxD не рівна 0 то закриваємо hVxD – дескриптор драйвера функцією CloseHandle.

  8. hVxD присвоюємо значення NULL.

Покроковий хід роботи для написання програми функцій класу:

  1. Функція ініціалізації порту InitPort () типу bool для класу CIO32.

  2. Завантажуємо драйвер функцією CreateFile з параметрами\\\\.\\io32port.vxd для активізації порту, 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL і присвоюємо його дескриптор змінній hVxD.

  3. Якщо драйверу hVxD передано системне значення INVALID_HANDLE_VALUE - не доступний, виходимо і повертаємо значення falce.

  4. Повертаємо значення falce.

  1. Функція читання inPort типу bool з параметрами wPort типу WORD, pdwValue типу PDWORD і bSize типу BYTE для класу CIO32.

  2. Якщо драйвер hVxD рівний значенню NULL - не доступний, виходим і повертаємо значення falce.

  3. Задаємо величину dwReturn типу DWORD.

  4. Формуємо тег порту port типу tagPort32.

  5. Елементу bSize структури port присвоюємо значення bSize.

  6. Елементу wPort структури port присвоюємо значення wPort.

  7. Читаємо значення з указаного порту:

  8. Через функцію DeviceIOControl з параметрами: hVxD, IO32_READPORT, &port, sizeof (tagPort32), pdwValue, sizeof (DWORD), &dwReturn, NULL читаємо і повертаємо значення у функцію класу.

  1. Функція запису outPort типу bool з параметрами wPort типу WORD, pdwValue типу PDWORD і bSize типу BYTE для класу CIO32.

  2. Якщо драйвер hVxD рівний значенню NULL - не доступний, виходим і повертаємо значення falce.

  3. Задаємо величину dwReturn типу DWORD.

  4. Формуємо тег порту port типу tagPort32.

  5. Елементу bSize структури port присвоюємо значення bSize.

  6. Елементу dwValue структури port присвоюємо значення dwValue.

  7. Елементу wPort структури port присвоюємо значення wPort.

  8. Записуємо значення у вказаний порт:

  9. Через функцію DeviceIOControl з параметрами: hVxD, IO32_WRITEPORT, &port, sizeof (tagPort32), pdwValue, sizeof (DWORD), &dwReturn, NULL - читаємо і повертаємо значення у функцію класу.

Тепер ми маємо повноцінний клас роботи з портами. Спочатку потрібно викликати функцію Init Port для завантаження віртуального драйвера пристрою. Після неї можна читати любі існуючі в системі порти вводу-виводу. Кожна із функцій inPort outPort має по три аргументи: перший вказує номер порту; другий – призначений для передачі і отримання значень із порту; третій - визначає розмір даних передавання. Драйвер підтримує 4 типи даних: байт (1), слово (2), трьохбайтове значення (3) і подвійне слово (4). Наведемо приклад роботи з класом CIO32.

Покроковий хід роботи для написання програми роботи з класом CIO32:

  1. Об’являємо клас CIO32 системного типу io.

  2. Ініціалізуємо драйвер функцією initPort () як елемент класу io (через операцію «.» як посилання на елемент класу).

  3. Тепер можна працювати з портом:

  4. для прикладу включимо системний динамік і після 4 секунд виключимо:

  5. Ініціалізуємо змінну dwResult типу DWORD.

  6. Читаємо стан порту функцією inPort з параметрами: 0x61 – код порту, &dwResult – посилання на змінну результату і параметром 1 – 1байт, як елемент класу io.

  7. Включаємо до змінної dwResult код 0x03 через операцію «|=».

  8. Записуємо значення в порт функцією outPort з параметрами 0x61 – код порту, dwResult – посилання на змінну результату і параметром 1 – 1байт, як елемент класу io.

  9. Пауза 4 секунди командою Sleep з параметром 4000.

  10. Читаємо стан порту функцією inPort з параметрами: 0x61 – код порту, &dwResult – посилання на змінну результату і параметром 1 – 1байт, як елемент класу io.

  11. Включаємо до змінної dwResult код 0x03 через операцію «|=».

  12. Записуємо значення в порт функцією outPort з параметрами: 0x61 – код порту, &dwResult – посилання на змінну результату і параметром 1 – 1байт, як елемент класу io.

Варіанти практичних завдань: Написати фрагменти програм і функцій на мові С++.

Варіант

Завдання

Варіант 1

Клас CIO32 з використанням драйвера для запису даних в порт.

Варіант 2

Програма файлу ІО32.срр

Варіант 3

Функція ініціалізації порту InitPort () для класу CIO32.

Варіант 4

Функція читання з порту inPort() для класу CIO32.

Варіант 5

Функція запису в порт outPort() для класу CIO32.

Варіант 6

Програма роботи із класом CIO32.

Практичне завдання 10

Тема: Недокументований доступ до портів

Мета: вміти застосовувати основні поняття операційної системи при недокументованому доступі до портів.

Теоретичні відомості.

В нашому випадку прийдеться використовувати захисний режим, в якому розподіляються виконуючі задачі разом з використовуючими даними. В сегментні регістри замість реальних адрес завантажувати так звані селектори, тобто вказівник на 8-байтовий блок пам’яті, який вміщає всю необхідну інформацію про сегмент. Всі ці блоки зібрані в таблиці глобальних (GDT – Global Descriptor Table) і локальних (Lokal Descriptor Table) дескрипторів. Крім того існує таблиця дескрипторів переривань (IDT - Interrupt Descriptor Table). Селектор складається із номера дескриптора (адреси) в таблиці (біти 3-15), типу таблиці (1- LTD, 0- GDT) (біт 2) і рівня привілей в бітах 0-1 (біти 0-3). Самий високий рівень привілей (00b) дозволяє програмі працювати на рівні ядра. Другий рівень (01b) дає повний доступ до апаратури. Третій рівень (10b) і четвертий (11b) управляють різними прикладними програмами і розширеннями. Для доступу до портів вводу-виводу потрібно виходити на самий високий рівень – нульовий, для чого потрібно отримати методом перебору вільний дескриптор.

Далі розберемо використання найвищого рівня привілей тільки для операційних систем Windows 95/98/ME. Напишемо ще один клас для прямого доступу до портів вводу-виводу. В нижченаведених програмах ІО32_0 представлені відповідні підходи, які дозволять працювати напряму з будь-якими пристроями в операційних системах Windows 95/98/ME.

Покроковий хід роботи для написання програми класу IO32_0 для прямого доступу до портів вводу-виводу:

  1. Об’являємо клас IO32_0 для прямого доступу до портів вводу-виводу.

  2. Загальнодоступні конструктори і функції - public:

  3. Конструктор IO32_0 ().

  4. Деструктор ~IO32_0 ().

  5. Загальні функції:

  6. Прочитати значення із порту функцією inPort типу bool з параметрами: wPort типу WORD, pdwValue типу PDWORD, bSize BYTE, які були розглянуті на попередній практичній роботі у класовій програмі.

  7. Записати значення в порт функцією outPort типу bool з параметрами: wPort типу WORD, pdwValue типу PDWORD, bSize BYTE, які були розглянуті на попередній практичній роботі у класовій програмі.

  8. Недоступні функції – private тобто закрита частина класу:

  9. Реалізація дії pack (1) як константи чи макроса через директиву #pragma.

  10. Об’явлення структури для пошуку дескриптора в таблиці:

  11. Об’являємо структуру_GDT як складову класу командою typedef.

  12. Об’являємо змінну Limit типу WORD для ліміту значення дескриптора.

  13. Об’являємо змінну бази Base типу DWORD.

  14. Закінчуємо структуру. Зразок структури GDT.

  15. Опис дескриптора для сегменту даних:

  16. Об’являємо структуру_GDT_HANDLE як складову класу командою typedef.

  17. Об’являємо елемент структури L_0_15 типу WORD - біти 0-15 ліміту.

  18. Об’являємо елемент структури В_0_15 типу WORD - біти 0-15 бази сегменту.

  19. Об’являємо елемент структури В_16_23 типу BYTE - біти 16-23 бази сегменту.

  20. Об’являємо елемент структури Access : 4 типу BYTE – для доступу до сегменту.

  21. Об’являємо елемент структури Type : 1 типу BYTE - тип сегменту 1- код 0- дані.

  22. Об’являємо елемент структури DPL : 2 типу BYTE - рівень привілей для дескриптора сегменту.

  23. Об’являємо елемент структури IsRead : 1 типу BYTE - перевірка наявності сегменту.

  24. Об’являємо елемент структури L_16_19 типу BYTE - біти 16-19 ліміту.

  25. Об’являємо елемент структури OS : 1 типу BYTE - визначається операційною системою.

  26. Об’являємо елемент структури RSV_NULL : 1 типу BYTE – резерв.

  27. Об’являємо елемент структури B_32is16 : 1 типу BYTE – розрядність (1- 32-розрядний сегмент, 0- 16-розрядний).

  28. Об’являємо елемент структури L_Granul : 1 типу BYTE: 1- 4Кб 0 - в байтах.

  29. Об’являємо елемент структури B_24_31 типу BYTE - біти 24-31 бази сегменту.

  30. Закінченняструктури. Структура зразка GDT_HANDLE.

  31. Опис дескриптора шлюзу:

  32. Об’являємо структуру_Sluice_Handle як складову класу командою typedef.

  33. Об’являємо елемент структури Task_Offset_0_15 типу WORD - молодше слово зміщення для шлюзу задачі.

  34. Об’являємо елемент структури Segment_S типу WORD - селектор сегменту.

  35. Об’являємо елемент структури DWORD_Count : 5 типу WORD - число подвійних слів для роботи стеку.

  36. Об’являємо елемент структури NULL_5_7 : 3 типу WORD рівне 0.

  37. Об’являємо елемент структури Access : 4 типу WORD - доступ до сегменту.

  38. Об’являємо елемент структури Type : 1 типу WORD - тип шлюзу.

  39. Об’являємо елемент структури DPL : 2 типу WORD - рівень привілей для дескриптора шлюзу.

  40. Об’являємо елемент структури IsRead : 1 типу WORD - перевірка наявності сегменту.

  41. Об’являємо елемент структури Task_Offse_16_31 типу WORD - старше слово зміщення для шлюзу зада.

  42. Закінченняструктури. Структура зразка Sluice_HANDLE.

  43. Реалізація дії pack () як константи чи макроса через директиву #pragma.

  44. Функція доступу до портів:

  45. Об’являємо функцію ClassAccess в класі типу bool з параметрами: FunctionAddress типу PVOID, wPort типу WORD, pdwValue типу PDWORD, bSize типу BYTE.

  46. Закінчення класу.

Покроковий хід роботи для написання програми файлу IO32_0.cpp:

  1. Включаємо бібліотечний файл "stdafx.h".

  2. Включаємо клас "IO32_0.h".

  3. Реалізація класу:

  4. Підключаємо функцію коду класу IO32_0 :: IO32_0 () до його реалізації.

  5. Об’являємо пустий конструктор.

  6. Потрібні дві функції для обробки портів вводу-виводу:

  1. Функція як елемент класу __declspec з параметром naked і функція для запису в порт _outPortValue () типу повернення void.

  2. Підключаємо асемблер командою _asm.

  3. Порівнюємо (cmp), якщо розмір даних cl рівний 1 байту.

  4. Записуємо байт _Byte.

  5. Порівнюємо, якщо розмір даних cl рівний слову – 2 байти.

  6. Записуємо слово _Word.

  7. Порівнюємо, якщо розмір даних cl рівний подвійному слову – 4 байти.

  8. Записуємо подвійне слово _Dword.

  9. Обробляємо тип _Byte:

  10. Заносимо в регістр al значення [ebx].

  11. Записуємо байт al в порт dx.

  12. Виходим командою retf.

  13. Обробляємо тип _Word:

  14. Заносимо в регістр aх значення [ebx].

  15. Записуємо байт в порт dx.

  16. Виходим командою retf.

  17. Обробляємо тип _Dword:

  18. Заносимо в регістр еaх значення [ebx].

  19. Записуємо байт е в порт dx.

  20. Виходим командою retf.

  21. Кінець функції:

  1. Функція для читання з порту як елемент класу __declspec з параметром naked і функція _inPortValue () типу повернення void.

  2. Підключаємо асемблер командою _asm.

  3. Порівнюємо (cmp), якщо розмір даних cl рівний 1 байту

  4. Записуємо байт _Byte.

  5. Порівнюємо, якщо розмір даних cl рівний слову – 2 байти.

  6. Записуємо слово _Word.

  7. Порівнюємо, якщо розмір даних cl рівний подвійному слову – 4 байти.

  8. Записуємо подвійне слово _Dword.

  9. Обробляємо тип _Byte:

  10. Читаємо в регістр al байт із порту dx.

  11. Записуємо байт al в порт [ebx].

  12. Виходим командою retf.

  13. Обробляємо тип _Word:

  14. Читаємо в регістр aх слово із порту dx.

  15. Записуємо слово [ebx] в порт al.

  16. Виходим командою retf.

  17. Обробляємо тип _Dword:

  18. Читаємо подвійне слово dx із порту eax.

  19. Записуємо слово [ebx] в порт еaх.

  20. Виходим командою retf.

  21. Кінець функції:

  1. Функція пошуку вільного дескриптора в системі:

  2. Функція CallAccess класу IO32_0 типу bool з параметром FunctionAddress типу PVOID, wPort типу WORD, pdwValue типу PDWORD і bSize типу BYTE.

  3. Об’являємо лічильник циклів wNum = 1 типу WORD.

  4. Об’являємо масив Addr [3] - адреса для нашої задачі типу WORD.

  5. Об’являємо змінну регістру gdt типу GDT.

  6. Створюємо посилання *pHANDLE на тип GDT_HANDLE.

  7. Отримуємо регістр GDTR, починаючи з 286 процесора _asm sgdt [gdt]:

  8. Переходимо до другого дескриптора сегменту даних:

  9. Через посилання GDT_HANDLE*на адресу початку структури gdt і з’єднання з елементом Base через операцію «.», добавляємо 8 і присвоюємо значення змінній pHANDLE.

  10. Виконуємо пошук вільного дескриптора в таблиці GDT:

  11. Організовуємо цикл «для» по змінній wNum = 1 до < (gdt.Limit / 8 з наростанням wNum++.

  12. Якщо вказані поля структури рівні 0, вільний дескриптор знайдений:

  13. Якщо pHANDLE->IsRead=0 і pHANDLE->DPL=0 і pHANDLE->Type=0 і pHANDLE->Access=0, де операція «->» - означає з’єднання з елементом підструктури із його значенням.

  14. Визначаємо параметри Sluice_Handle для дескриптора шлюзу через посилання на *pSluice (взяття початкової адреси).

  15. Параметру pSluice присвоюємо pHANDLE через посилання на тип (Sluice_Handle*).

  16. Молодше слово адреси нашої функції:

  17. Елемент Task_Offset_0_15 структури pSluice, який з’єднується для присвоєння йому значення операцією «.», отримує значення LOWORD (FunctionAddress).

  18. Селектор сегменту з найвищим рівнем привілей:

  19. Елемент Segment_S структури pSluice, з’єднаний для присвоєння йому адреси 0x28.

  20. Елемент DWORD_Count структури pSluice, з’єднаний для присвоєння йому значення 0.

  21. Елемент NULL_5_7 структури pSluice, з’єднаний для присвоєння йому значення 0.

  22. Елемент Access структури pSluice, з’єднаний для присвоєння йому значення 0x0C.

  23. Елемент Type структури pSluice, з’єднаний для присвоєння йому значення 0.

  24. Елемент DPL структури pSluice, з’єднаний для присвоєння йому значення 3.

  25. Елемент IsRead структури pSluice, з’єднаний для присвоєння йому значення 1.

  26. Старше слово адреси нашої функції:

  27. Елемент Task_Offset_16_31 структури pSluice, який з’єднується для присвоєння йому значення операцією «->», отримує значення HIWORD (FunctionAddress).

  28. Заповнюємо адресу для подальшого виклику:

  29. 0-й елемент масиву Addr отримує адресу 0x00.

  30. 1-й елемент масиву Addr отримує адресу 0x00.

  31. 2-й елемент масиву Addr отримує адресу (wNum<<3) | 3 через побітове додавання «|» і операцію зсуву << вліво.

  32. Передаємо наші параметри:

  33. Підключаємо асемблер командою _asm.

  34. Переміщаємо в регістр ebx значення передавання [pdvValue].

  35. Переміщаємо в регістр cl значення передавання [bSize] - розмір значення передавання.

  36. Переміщаємо в регістр dx номер порту вводу-виводу [wPort].

  37. Виконуємо подальший виклик call fword зформованих параметрів в масиві ptr [Addr].

  38. Обнулюємо дескриптор.

  39. Здійснюємо призначення пам’яті командою memset з параметрами pHANDLE, 0, 8.

  40. Виходим із функції з поверненням значення true.

  41. Перевіряємо наступний дескриптор:

  42. Збільшуємо значення дескриптора pHANDLE на 1.

  43. Не знайдено ні одного вільного дескриптора, повертаємо значення false.

  44. Кінець функції.

  1. Загальнодоступні функції вводу-виводу:

  2. Функція запису в порт outPort класу IO32_0 типу повернення bool з параметрами: wPort типу WORD, dwValue типу DWORD, bSize BYTE.

  3. Виклик функції CallAccess з параметрами: _outPortValue класової структури типу (PVOID), wPort, взяттям адреси на dwValue і bSize) здійснює повернення значення функції.

  4. Функція читання з порта inPort класу IO32_0 типу повернення bool з параметрами: wPort типу WORD, dwValue типу DWORD, bSize BYTE.

  5. Виклик функції CallAccess з параметрами: _inPortValue класової структури типу (PVOID), wPort, взяттям адреси на pdwValue і bSize) здійснює повернення значення функції.

  6. Кінець загальнодоступних функцій:

Принцип роботи класу ІО32_0 побудований за наступним алгоритмом: спочатку виконуємо пошук вільного дескриптора в таблиці GDT. Для отримання початкового вказівника на таблицю застосовуємо команду sgdt, шукаючи вільного дескриптора. Якщо дескриптор знайдений, присвоюємо йому вказівник на заповнений дескриптор шлюзу (SluiceHandle). Через структуру шлюзу визначаємо адресу функції, на яку буде передано управління шляхом дальнього виклику, а також загальні параметри доступу і рівень привілей. Далі записуємо аргументи для функції і безпосередньо виконуємо дальній виклик, який буде оброблений процесором з максимальним пріоритетом. Таким чином отримуємо доступ до апаратних портів, і операційна система не буде мішати цьому.

Варіанти практичних завдань. Написати фрагменти програм і функцій на мові С++.

Варіант

Завдання

Варіант 1

Написати програми класу IO32_0 для прямого доступу до портів вводу-виводу.

Варіант 2

Написати програму функції запису в порт _outPortValue ().

Варіант 3

Написати програму функції читання з порту _inPortValue ().

Варіант 4

Написати програму функції пошуку вільного дескриптора CallAccess класу IO32_0.

Варіант 5

Сформулювати загальнодоступні функції вводу-виводу та структуризувати файл IO32_0.cpp.

Додаток А. Програмні коди функцій користувача, які можуть використовуватися при читанні і записі інформації на зовнішніх пристроях.

/*Програмний код функції для читання інформації з пристрою з використанням бібліотечних системних функцій читання.*/

// підключаємо необхідний файл ресурсів з бібліотеки.

#include <conio.h>

// прочитаємо значення базової пам’яті в кілобайтах.

іnt GetBaseMemory ()

{

//об’являємо змінні для отримання старшого і молодшого байтів.

BYTE lowBase = 0, highBase = 0;

//читаємо інформацію з CMOS-пам’яті

_outp (0x70, 0x15); //записуємо номер першого регістру

lowBase = _inp (0x71); //читаємо молодший біт

_outp (0x70, 0x16); // записуємо номер другого регістру

highBase = _inp (0x71); //читаємо старший байт

// повертаємо розмір базової пам’яті в кілобайтах

return ((highBase << 8) | lowBase);

}

// функція користувача для управління клавіатурою.

void KeyBoardOnOff (bool bOff)

{

BYTE state; //текучий стан

if (bOff) // виключити клавіатуру

{

//отримуємо текучий стан

state = _inp (0x61);

//встановлюємо біт 7 в 1

state |=0x80;

// записуємо обновлене значення в порт

_outp (0x61, state);

}

}

/* Функція користувача для скидання даних пристрою АТА/АТАРІ з використанням бібліотечних функцій запису.*/

bool ResetDrive ()

{

// перший пристрій на другому каналі (як правило CD-ROM)

_outp (0x177, 0x08); // пишемо команду скидання 08h

//перевіряємо результат виконання

for (int i=0; i<5000, i++)

{

//перевіряємо біт 7 BUSY

if (( _inp (0x177) & 0x80) == 0x00)

return true; //команда успішно завершена

}

return false; //

}

/*Функція користувача для управління лотком CD-ROM і використанням бібліотечних функцій запису.*/

void Eject ( bool bOpen )

{

int iTimeWait = 50000;

//формат пакетної команди для відкриття лотка

WORD Eject [6] = { 0x1B, 0, 2, 0, 0, 0 };

// формат пакетної команди для закриття лотка

WORD Close [6] = { 0x1B, 0, 3, 0, 0, 0 };

//перевіряємо готовність пристрою

while ( -- iTimeWait > 0)

{

//читаємо стан порту

if ((_inp (0x177) & 0x80 == 0x00) && (_inp (0x177) & 0x08 == 0x00)) break;

//закінчився час очікування

if (iTimeWait < 1) return;

}

//вибираємо перший пристрій на другому каналі

_outp ( 0x176, 0xA0);

//перед посилкою пакетної команди потрібно перевірити стан

iTimeWait = 50000;

//очікуємо готовність пристрою

while ( -- iTimeWait > 0)

{

//читаємо стан порту

if ((_inp (0x177) & 0x80 == 0x00) && (_inp (0x177) & 0x08 == 0x00)) break;

//закінчився час затримки

if (iTimeWait < 1) return;

}

// пишемо в порт команду пакетної передачі A0h

_outp ( 0x177, 0xA0);

//очікуємо готовність пристрою

while ( -- iTimeWait > 0)

{

//читаємо стан порту

if ((_inp (0x177) & 0x80 == 0x00) && (_inp (0x177) & 0x08 == 0x01)) break;

// закінчився час затримки

if (iTimeWait < 1) return;

}

//пишемо в порт пакетну команду

if (bOpen) //відкрити лоток

{

for (int i=0; i>6; i++)

{

_outpw (0x170, Eject[i]); //12-байтова команда

}

}

else //закрити лоток

{

for (int j=0; j>6; j++)

{

_outpw (0x170, Close[j]); //12-байтова команда

}

}

//перевіряємо результат виконання команди, якщо потрібно

iTimeWait = 50000;

//очікуємо готовність пристрою

while ( -- iTimeWait > 0)

{

//читаємо стан порту

if ((_inp (0x177) & 0x80 == 0x00) && (_inp (0x177) & 0x01 == 0x00)) &&

(_inp (0x177) & 0x40 == 0x01)) break;

// закінчився час затримки

if (iTimeWait < 1) return;

}

}

Додаток Б. Функції користувача для читання сектора з диску і запису на диск з використанням функції DeviceIoControl.

//Функція користувача для читання сектора з диску.

#include "stdafx.h"

#define VWIN32_DIOC_DOS_DRIVEINFO 6 // код функції драйвера

#define CF_FLAG 1 //прапор переносу

//додаткові структури

typedef struct _DIOC_REGISTERS

{

DWORD reg_EBX;

DWORD reg_EDX;

DWORD reg_ECX;

DWORD reg_EAX;

DWORD reg_EDI;

DWORD reg_ESI;

DWORD reg_Flags;

} DIOC_REGISTERS;

#pragma pack (1)

typedef struct _DATABLOCK

{

DWORD dwStartSector; //номер початкового сектору

WORD wNumSectors; //кількість секторів

DWORD pBuffer; //вказівник на буфер даних

} DATABLOCK;

#pragma pack ()

//Функція для читання секторів з диска:

bool ReadSector (unsigned int uDrive, DWORD dwStartSector, WORD wNumSectors, LPBYTE lpBuffer)

{

HANDLE hDriver;

DIOC_REGISTERS reg={0};

DATABLOCK data = {0};

bool bResult;

DWORD dwResult = 0;

//ініціалізуємо драйвер

hDriver = CreateFile (\\\\.\\vwin32, 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, 0);

//якщо драйвер недоступний – виходимо з функції

if (hDriver == INVALID_HANDLE_VALUE) return falce;

//заповнюємо структуру даних DATABLOCK

data.dwStartSector = dwStartSector;

data.wNumtSector = wNumSector;

data.pBuffer = (DWORD) lpBuffer;

//заповнюємо структуру управління

reg.reg_EAX = 0x7305 // функція 7305h переривання 21h

reg.reg_EBX = (DWORD) &data;

reg.reg_ECX = -1;

reg.reg_EDX = uDrive; //номер логічного диску

//викликаємо функцію DeviceIoControl

bResult =DeviceIoControl (hDriver, VWIN32_DIOC_DOS_DRIVEINFO, &reg, sizeof (reg), &reg, sizeof (reg), &dwResult, 0);

//якщо виникла помилка – виходимо із функції

if (!bResult || (reg.reg_Flags &CF_FLAG))

{

CloseHandle (hDriver);

return false;

}

return true;

}

//Приклад функції ReadSector для читання двох секторів диска:

//номер секторів може бути: 0- по замовчуванню, 1- А, 2- В, 3- С, 4-D, 4-E, і //т.д.

//виділяємо пам’ять для двох секторів жорсткого диску

char *buffer = NULL;

buffer =new char [512*2];

//викликаємо функцію читання секторів

ReadSector (3, 0, 2, (LPBYTE) buffer);

//звільняємо пам’ять

delete [] buffer;

//Функція користувача для запису сектора на диск.

//функція для запису сектора диску:

bool WriteSector (unsigned int uDrive, DWORD dwStartSector, WORD wNumSectors, LPBYTE lpBuffer)

{

HANDLE hDriver;

DIOC_REGISTERS reg = {0}; // структура управління

DATABLOCK data = {0}; // структура даних

bool bResult;

DWORD dwResult = 0;

//ініціалізуємо драйвер

hDriver=CreateFile (\\\\.\\vwin32, 0,0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, 0);

//якщо драйвер недоступний, виходимо з функції

if (hDriver==INVALID_HANDLE_VALUE) return falce;

//заповнюємо структуру даних DATABLOCK

data.dwStartSector = dwStartSector;

data.wNumtSector = wNumSector;

data.pBuffer = (DWORD) lpBuffer;

//заповнюємо структуру управління

reg.reg_EAX = 0x7305 //функція 7305h переривання 21h

reg.reg_EBX = (DWORD) &data;

reg.reg_ECX = -1;

reg.reg_EDX = uDrive; //номер логічного диску

reg.reg_ESI =0x6001;

//викликаємо функцію DeviceIoControl

bResult =DeviceIoControl (hDriver, VWIN32_DIOC_DOS_DRIVEINFO, &reg, sizeof (reg), &reg, sizeof (reg), &dwResult, 0);

//якщо виникла помилка – виходимо із функції

if (!bResult || (reg.reg_Flags &CF_FLAG))

{

CloseHandle (hDriver);

return false;

}

return true;

}

Додаток В. Класи для читання і запису інформації з використанням драйвера для роботи в Windows 98/ME з портами.

// Клас IO32.h: interface для класу CIO32.

#include <winioctl.h>

// визначаємо коди функцій для читання і запису

#define IO32_WRITEPORT CTL_CODE (FILE_DEVICE_UNKNOWN, 1, \METHOD_NEITHER,

FILE_ANY_ACCESS)

#define IO32_READPORT CTL_CODE (FILE_DEVICE_UNKNOWN, 2, \METHOD_NEITHER,

FILE_ANY_ACCESS)

// об’являємо клас

class CIO32

{

public:

CIO32 ();

~CIO32 ();

//загальні функції

bool InitPort (); //ініціалізація драйвера

//функція для зчитування значення із порту

bool inPort (WORD wPort, PDWORD pdwValue, BYTE bSize);

//функція для запису значення в порт

bool outPort (WORD wPort, PDWORD pdwValue, BYTE bSize);

private:

//закрита частина класу

HANDLE hVxD; //дескриптор драйвера

//структура управління

#pragma pack (1)

strukt tagPort32

{

USHORT wPort;

ULONG dwValue;

UCHAR bSize;

};

#pragma pack ()

}; //закінчення класу

//Приведемо файл ІО32.срр

#include "stdafx.h"

#include "IO32.h"

//реалізація класу CIO32

//конструктор

CIO32 :: CIO32 ()

{

hVxD = NULL;

}

//деструктор

CIO32 :: ~CIO32 ()

{

if (hVxD) CloseHandle (hVxD);

hVxD = NULL;

}

//Функції:

bool CIO32 :: InitPort () //Функція ініціалізації порта

{

//завантажуємо драйвер

hVxD = CreateFile (\\\\.\\io32port.vxd, 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL);

//якщо драйвер не доступний, виходим if (hVxD == INVALID_HANDLE_VALUE) return falce;

return true;

}

//функція читання з порту.

bool CIO32 :: inPort (WORD wPort, PDWORD pdwValue, BYTE bSize)

{

// якщо драйвер не доступний, виходим if (hVxD == NULL) return falce;

DWORD dwReturn;

tagPort32 port;

port.bSize = bSize;

port.wPort = wPort;

//читаємо значення з указаного порту

return DeviceIOControl (hVxD, IO32_READPORT, &port, sizeof (tagPort32), pdwValue, sizeof (DWORD), &dwReturn, NULL) ;

}

//функція запису в порт.

bool CIO32 :: outPort (WORD wPort, DWORD dwValue, BYTE bSize)

{

// якщо драйвер не доступний, виходим

if (hVxD == NULL) return falce;

DWORD dwReturn;

tagPort32 port;

port.bSize = bSize;

port.dwValue = dwValue;

port.wPort = wPort;

//записуємо значення в указаний порт

return DeviceIOControl (hVxD, IO32_WRITEPORT, &port, sizeof (tagPort32), NULL, 0, &dwReturn, NULL) ;

}

//Приклад роботи з класом CIO32:

//об’являємо клас

CIO32 io;

//ініціалізуємо драйвер

io.initPort ();

//тепер можна працювати з портом

//для прикладу включимо системний динамік і після 4 секунд виключимо

DWORD dwResult = 0;

//читаємо стан порту

io.inPort (0x61, &dwResult, 1);

dwResult |= 0x03; //включаємо

//записуємо значення в порт

io.outPort (0x61, dwResult, 1);

//пауза 4 секунди

Sleep (4000);

//читаємо стан порту

io.inPort (0x61, dwResult, 1);

dwResult &= 0xFC; //виключаємо

//записуємо значення в порт

io.outPort (0x61, dwResult, 1);

Додаток Г. Приклади програм класів недокументованого доступу до портів в операційних системах Windows 95/98/ME.

//Об’являємо клас IO32_0.

class IO32_0

{

public:

IO32_0 () //конструктор

~IO32_0 () //деструктор

//загальні функції

//прочитати значення із порту

bool inPort (WORD wPort, PDWORD pdwValue, BYTE bSize);

//записати значення в порт

bool outPort (WORD wPort, DWORD dwValue, BYTE bSize);

private:

#pragma pack (1)

//об’являємо структури

//структура для пошуку дескриптора в таблиці

typedef struct _GDT

{

WORD Limit; //ліміт

DWORD Base; //база

} GDT;

//опис дескриптора для сегменту даних

typedef struct _GDT_HANDLE

{

WORD L_0_15; //біти 0-15 ліміту

WORD B_0_15; //біти 0-15 бази сегменту

BYTE B_16_23; //біти 16-23 бази сегменту

BYTE Access : 4; //доступ до сегменту

BYTE Type : 1; //тип сегменту 1- код 0- дані

BYTE DPL : 2; //рівень привілей для дескриптора сегменту

BYTE IsRead : 1; //перевірка наявності сегменту

BYTE L_16_19; //біти 16-19 ліміту

BYTE OS : 1; //визначається операційною системою

BYTE RSV_NULL : 1; //резерв

BYTE B_32is16 : 1; //розрядність (1- 32-розрядний сегмент, 0- 16-розрядн.

BYTE L_Granul : 1; //1- 4Кбб 0- в байтах

BYTE B_24_31; //біти 24-31 бази сегменту

} GDT_HANDLE;

//опис дескриптора шлюзу

typedef struct _Sluice_Handle;

{

WORD Task_Offset_0_15; //молодше слово зміщення для шлюзу задачі

WORD Segment_S; //селектор сегменту

WORD DWORD_Count : 5; //число подвійних слів для роботи //стеку

WORD NULL_5_7 : 3; //рівне 0

WORD Access : 4; //доступ до сегменту

WORD Type : 1; //тип шлюзу

WORD DPL : 2; //рівень привілей для дескриптора шлюзу

WORD IsRead : 1; //перевірка наявності сегменту

WORD Task_Offse_16_31; //старше слово зміщення для //шлюзу задачі

} Sluice_Handle;

#pragma pack ()

//функція доступу до портів

bool ClassAccess (PVOID FunctionAddress, WORD wPort, PDWORD pdwValue, BYTE bSize);

}; //кінець класу

// Файл IO32_0.cpp

#include "stdafx.h"

#include "IO32_0.h"

//реалізація класу

IO32_0 :: IO32_0 ()

{

//пустий конструктор

}

//дві функції для обробки портів вводу-виводу:

//функція запису в порт.

__declspec (naked) void _outPortValue ()

{

_asm

{

cmp cl, 1 //якщо розмір даних рівний байту

je _Byte //записуємо байт

cmp cl, 2 //якщо розмір даних рівний слову

je _Word //записуємо слово

cmp cl, 4 //якщо розмір даних рівний подвійному слову

je _Dword //записуємо подвійне слово

_Byte:

mov al, [ebx]

out dx, al //записуємо байт в порт

retf //виходим

_Word:

mov ax, [ebx]

out dx, ax //записуємо слово в порт

retf //виходим

_Dword:

mov eax, [ebx]

out dx, eax //записуємо подвійне слово в порт

retf //виходим

}

}

//функція читання з порта.

__declspec (naked) void _inPortValue ()

{

_asm

cmp cl, 1 //якщо розмір даних рівний байту

je _Byte //читаєм байт

cmp cl, 2 // якщо розмір даних рівний слову

je _Word //читаємо слово

cmp cl, 4 // якщо розмір даних рівний подвійному слову

je _Dword //читаєм подвійне слово

_Byte:

in al, dx //читаємо байт із порту

mov [ebx], al

retf

_Word:

In ax, dx //читаємо слово із порту

mov [ebx], ax

retf

_Dword:

In eax, dx //читаємо подвійне слово із порту

mov [ebx], eax

retf

}

}

//Функція пошуку вільного дескриптора в системі.

bool IO32_0 :: CallAccess (PVOID FunctionAddress, WORD wPort, PDWORD pdwValue, BYTE bSize)

{

WORD wNum = 1; //лічильник циклів

WORD Addr [3]; //адреса для нашої задачі

GDT gdt;

GDT_HANDLE *pHANDLE;

// отримуємо регістр GDTR, починаючи c 286 процесора

_asm sgdt [gdt]

// переходимо до другого дескриптора сегменту даних

pHANDLE = (GDT_HANDLE*) (gdt.Base +8);

// виконуємо пошук вільного дескриптора в таблиці GDT

for (wNum = 1; wNum < (gdt.Limit / 8); wNum++)

{

//якщо вказані поля структури рівні 0б

//вільний дескриптор знайдений

if (pHANDLE->IsRead==0 && pHANDLE->DPL==0 && pHANDLE->Type==0 && pHANDLE->Access==0)

{

//визначаємо параметри для дескриптора шлюзу

Sluice_Handle *pSluice;

pSluice = (Sluice_Handle*) pHANDLE;

//молодше слово адреси нашої функції

pSluice->Task_Offset_0_15=LOWORD (FunctionAddress);

//селектор сегменту з найвищим рівнем привілей

pSluice->Segment_S=0x28;

pSluice->DWORD_Count=0;

pSluice->NULL_5_7=0;

pSluice->Access=0x0C;

pSluice->Type=0;

pSluice->DPL=3;

pSluice->IsRead=1;

//старше слово адреси нашої функції

pSluice->Task_Offse_16_31=HIWORD (FunctionAddress);

//заповнюємо адресу для подальшого виклику

Addr[0]=0x00;

Addr[1]=0x00;

Addr[2]=(wNum<<3) | 3;

//передаємо наші параметри

_asm

{

mov ebx, [pdvValue] //значення передавання

mov cl, [bSize] //розмір значення передавання

mov dx, [wPort] //номер порту вводу-виводу

//виконуємо подальший виклик

call fword ptr [Addr]

}

//обнулюємо дескриптор

memset (pHANDLE, 0, 8);

return true; // виходим із функції

}

// перевіряємо наступний дескриптор

pHANDLE++;

}

//не знайдено ні одного вільного дескриптора

return false;

}

//Загальнодоступні функції вводу-виводу.

bool IO32_0 :: outPort (WORD wPort, DWORD dwValue, BYTE bSize)

{

return CallAccess ((PVOID) _outPortValue, wPort, &dwValue, bSize);

}

bool IO32_0 :: inPort (WORD wPort, PDWORD pdwValue, BYTE bSize)

{

return CallAccess ((PVOID) _inPortValue, wPort, pdwValue, bSize);

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]