Как изменить способности компьютера

Дата публикации 16 июн 2002

Как изменить способности компьютера — Архив WASM.RU

  Огромное количество микросхем от "простеньких", выполняющих логические операции типа И,ИЛИ,НЕ и др., до сверхинтегрированных контроллеров в которые "впихнута" чуть ли не вся машина (ПЭВМ). Всевозможные дополнительные платы (адаптеры, контроллеры), винчестеры и т.д. Все это призвано к жизни и к взаимодействию с целью облегчить существование своим хозяевам (пользователям, программистам).

  Что же, картина получается непростая. Но говорить о сложности устройства компьютера, о сложности его частей, о сложности процессов их взаимодействия излишне - это и так понятно.

  Если Вы дочитали до этого места то, может быть, подумали: а к чему это автор намекает на всякие сложности, очевидные же вещи. Если появился вопрос - дадим на него ответ. Любой человек, имевший возможность работать с компьютером, и пользователь, и программист, в один прекрасный день думает: а хорошо бы, если бы компьютер ("ящик") умел делать еще и то-то, и так-то. Установить бы дополнительную "платочку" в компьютер и получить желаемое.

"Ну,"- скажет критически настроенный читатель, - "это же надо собирать какие-то контроллеры, разбираться в микросхемах, понимать в управляющих диаграммах, то есть быть, как минимум, схемотехником."
Он прав, но не совсем. Конечно хорошо быть схемотехником (автор по профессии схемотехник и утверждает это обоснованно), но не обязательно, а вот программистом, для того чтобы применить опыт, описываемый далее, быть нужно.

  Дело в том, что многие сетевые адаптеры имеют гнездо для установки ПЗУ (постоянного запоминающего устройства, rom, read only memory), позволяющего при наличии локальной сети осуществлять удаленную загрузку операционной системы. Но, как показала практика, в большинстве случаев эта возможность не используется. Если в это гнездо вместо микросхемы со старой ПЗУ (если она там вообще есть) установить новую, со своей программой, то компьютер обретет новые дополнительные свойства. Например, это может быть программа, защищающая компьютер от несанкционированного доступа, или антивирусная защита (монитор, ревизор и т.п.). Так что учиться схемотехнике не придется. Можно сразу приступить к разработке программы для ПЗУ.

  Но в чем же здесь преимущество? Одни только сложности. Вынимай микросхему, ищи новою. Программу нужно еще как-то поместить в ПЗУ. Да и вообще, будет ли она выполняться? Кто или что сделает так, чтобы эта программа получила управление?

  Вот именно об этом "механизме", заложенном в BIOS и обеспечивающем возможность выполнения программ, "прошитых" (так называют программы, записанные в ПЗУ с помощью программаторов) в ПЗУ, а также о том, каким образом необходимо оформлять эту программу, и хотелось рассказать заинтересованным читателям.

Отметим при этом, что преимущество программы, размещенной в ПЗУ, заключается в том, что она неуничтожаема и отработает еще задолго до того, как начнут свою работу по загрузке операционной системы с диска модули MAIN BOOT и BOOT. И хотя эти части системы наиболее часто подвержены атакам вирусов, мы хозяева положения и можем блокировать любую атаку или же осуществить восстановительные работы.

ВОЛШЕБНАЯ ВОЗМОЖНОСТЬ BIOS

  Каким же образом машина узнает, что ей необходимо выполнить еще одну дополнительную программу? Задавшись вопросом, начинаем искать ответ. К счастью есть палочка-выручалочка - листинг BIOS, поставляемый в комплекте (за отдельную плату) с "персоналками". В этом листинге содержится информация, раскрывающая тайны функционирования машины. В частности, и ответ на наш вопрос.

  Оказывается, что среди прочих важных дел, совершаемых подпрограммами BIOS, есть дело, заключающееся в проверке присутствия ПЗУ в адресном пространстве с С800:0 до E000:0.

  Это пространство сканируется с шагом в 2048 байт на предмет наличия "подписи" 0AA55H. Но ведь может быть и совпадение. Чтобы не ошибиться, после того, как найдена "подпись", производится расчет контрольной суммы методом сложения по модулю 100H (сложение побайтно без учета переноса). Результат при этом должен получиться равным нулю. Но сколько же байтов необходимо просуммировать? Эта информация содержится в одном байте, следующем за подписью. Причем чтобы узнать размер ПЗУ в байтах, необходимо умножить это число на 200H.

  Если все эти условия выполняются, BIOS считает, что перед ним ПЗУ, оформленное по правилам, и совершает последний шаг - передает управление на смещение +3 (отсчет начинается с нуля) относительно найденного сегмента. Следовательно, там должна располагаться первая выполняемая команда нашей программы. Обычно это команда jmp. Совершив все необходимое, наша программа должна вернуть управление обратно в BIOS с тем, чтобы та продолжила поиск аналогичных ПЗУ. Следовательно, наша программа должна заканчиваться командой retf.

  Для того, чтобы было более ясно, о чем шла речь, ниже приводятся фрагменты кода BIOS, реализующие необходимые проверки и передачу управления.

Код (Text):
  1.  
  2. ;####################################################################
  3. ; Поиск дополнительной ROM в области памяти C800->E000 в блоках по 2K
  4. ; Модуль ROM должен иметь подпись '55AA' в первых двух байтах и
  5. ; индикатор размера ((размер в байтах)/512) в третьем байте.
  6. ; Исполняемый код должен начинаться с четвертого байта.
  7. ;////////////////////////////////////////////////////////////////////
  8. ROM_SCAN:          ;начало фрагмента кода BIOS
  9.  STI                ;разрешить прерывания
  10.  MOV    DX,0C800H   ;начало сканирования
  11. ROM_SCAN2:
  12.  MOV    DS,DX       ;ds - сегмент очередного ROM-модуля
  13.  SUB    BX,BX       ;bx - указатель в ROM-модуле
  14.  MOV    AX,[BX]     ;взять первое слово в модуле
  15.  CMP    AX,0AA55H   ;подпись найдена?
  16.  JNZ    NEXT_ROM    ; нет - перейти к следующему базовому адресу ROM
  17.  CALL   ROM_CHECK   ; да - проверить контрольную сумму
  18.  JMP    ARE_WE_DONE ;перейти к проверке конца области дополнительной ROM
  19. NEXT_ROM:           ;следующий ROM-модуль
  20.  ADD    DX,0080H    ; позиционировать новый модуль через 2K байт (как сегмент)
  21. ARE_WE_DONE:        ;проверка конца области дополнительной ROM
  22.  CMP    DX,0E000H   ;достигнут адрес E0000?
  23.  JL     ROM_SCAN2   ; нет - перейти к новой проверке подписи
  24. ;////////////////////////////////////////////////////////////////////
  25. ;Последующий код не относится к теме статьи
  26. ;...
  27. ;...
  28. ;...
  29. ;####################################################################
  30. ;Эта подпрограмма вычисляет контрольную сумму дополнительных
  31. ;модулей ROM и, если она верна, вызывает программу из модуля
  32. ;(При входе регистр ES указывает на сегмент данных BIOS)
  33. ;////////////////////////////////////////////////////////////////////
  34.  ROM_CHECK PROC NEAR
  35.   ;вычислить число байт для сканирования
  36.   SUB    AH,AH           ;ax - индикатор размера модуля
  37.   MOV    AL,[BX+2]
  38.   MOV    CL,09H          ;ax=ax*512
  39.   SHL    AX,CL
  40.   MOV    CX,AX           ;cx - счетчик байт для сканирования
  41.   ;вычислить указатель на следующий модуль и поместить его в dx
  42.   PUSH   CX
  43.   MOV    CX,4
  44.   SHR    AX,CL
  45.   ADD    DX,AX
  46.   POP    CX
  47.   ;вычислить контрольную сумму модуля
  48.   CALL   ROM_CHECKSUM
  49.   JZ     ROM_CHECK_1
  50.   CALL   ROM_ERR         ;контрольная сумма на совпала!
  51.   JMP    ROM_CHECK_END
  52. ;---- Вызов программы, находящейся в ПЗУ ----------------
  53.  ROM_CHECK_1:
  54.   PUSH   DX              ;сохранить текущее значение указателя
  55.   MOV    ES:IO_ROM_INIT,0003H     ;подготовить смещение
  56.   MOV    ES:IO_ROM_SEG,DS         ;подготовить сегмент
  57.   CALL   DWORD PTR ES:IO_ROM_INIT ;вызвать программу
  58.   POP    DX              ;восстановить указатель
  59. ;----------------------------------------------------------
  60.  ROM_CHECK_END:
  61.   RET                    ;завершить работу
  62.  ROM_CHECK  ENDP
  63. ;===========================================
  64. ;Подпрограмма вычисления контрольной суммы
  65. ;===========================================
  66.  ROM_CHECKSUM PROC NEAR
  67.   XOR    AL,AL
  68.  NEXT_BYTE:
  69.   ADD    AL,[BX]
  70.   INC    BX
  71.   LOOP   NEXT_BYTE
  72.   OR     AL,AL           ;сумма равна нулю?
  73.   RET
  74.  ROM_CHECKSUM END
  75.  

  Осталось обговорить еще одну тонкость. Каким образом обеспечить равенство нулю контрольной суммы? Ведь очевидно, что само собой это условие не будет выполняться. Идея проста - необходимо дополнить получившуюся сумму до нуля. Делается это размещением числа, равного разнице между 100H и получившейся контрольной суммой в байте (перед расчетом он должен быть равен нулю), следующем за последней командой программы. Отметим также, что расчет контрольной суммы ведется по количеству байт, равных размеру ПЗУ, а не по размеру получившегося кода программы.

  После сказанного можно отобразить структуру программы следующим образом:

смещениекодкомментарий
+00db 55h,0AAhподпись
+02db (?)индикатор размера модуля
+03db 0EAh,(?),(?)передача управления коду (metka)
+06;...константы программы
+??metka: ;...код программы
+??retfвозврат управления BIOS
+??db (?)дополнение контрольной суммы до 100h
Знаки вопроса означают, что конкретные значения будут известны только после написания программы.
Если ваша программа должна работать только на этапе старта компьютера, то ее переменные вы можете размещать в любой области conventional ОЗУ выше той, которая отведена под переменные BIOS (то есть в диапазоне сегментных адресов 0050:0000...9000:FFFF) - на этом этапе указанный диапазон свободен. Гораздо сложнее ситуация, когда ваша программа должна работать после того, как операционная система загружена: например, обслуживать прерывания или предоставлять свои процедуры для вызова обычными программами. В этом случае вам придется предусмотреть механизм, исключающий конфликты использования памяти.

  В заключение - две программы.

Код (Text):
  1.  
  2. ;############################################################
  3. ;Пример программы рассчитывающей контрольную сумму,
  4. ;вычисляющей дополняющий байт и формирующей обрабатываемую
  5. ;программу в виде файла на диске.
  6. ;============================================================
  7. TITLE ComputeCRC
  8. INCLUDE MACRO.DEF
  9. LENGTHROM    EQU 2000H       ;Размер ПЗУ в байтах (8192)
  10. CODE SEGMENT BYTE PUBLIC
  11. ASSUME CS:CODE,DS:CODE
  12. ORG 100H
  13. START:
  14.   JMP SHORT BEGIN
  15.   ;Имя обрабатываемой программы
  16.   FileName: DB 'FileName.Ext',0
  17. BEGIN:
  18.   ;Открыть файл для чтения
  19.   MOV AX,3D02H
  20.   MOV DX,OFFSET FileName
  21.   INT 21H
  22.   JNC M1
  23.   JMP ER
  24. M1:
  25.   ;Прочитать файл в конец программы
  26.   MOV BX,AX
  27.   MOV AX,3F00H
  28.   MOV CX,LENGTHROM
  29.   MOV DX,OFFSET LastByte
  30.   INT 21H
  31.   JNC M2
  32.   JMP ER
  33. M2:
  34.   ;Запомнить количество прочитанных байт
  35.   MOV CX,AX
  36.   ;Установить указатель позиции в файле на начало
  37.   MOV AX,4200H
  38.   PUSHR CX,DX         ;PUSHR,POPR - макросы описаны в MACRO.DEF
  39.   MOV CX,0            ;см.статью Макросы First и Second
  40.   MOV DX,0
  41.   INT 21H
  42.   POPR CX,DX
  43.   JC ER
  44.   PUSHR AX,BX,CX,DX,DI
  45.   ;Рассчитать контрольную сумму
  46.   PUSHR CX
  47.   XOR AX,AX
  48.   MOV BX,OFFSET LastByte
  49. M3: ADD AL,[BX]
  50.   INC BX
  51.   LOOP M3
  52.   ;Вычислить дополняющий байт
  53.   MOV CX,100H
  54.   SUB CX,AX
  55.   MOV AX,CX
  56.   POPR CX
  57.   ;Настроить указатель на начало обрабатываемой программы
  58.   PUSHR AX
  59.   MOV DI,CX
  60.   ADD DI,OFFSET LastByte-1
  61.   ;Найти место в обрабатываемой программе для записи вычисленного байта
  62.   MOV AX,0
  63.   STD
  64.   REPNE SCASB
  65.   INC DI
  66.   POPR AX
  67.   ;Записать его туда
  68.   MOV [DI],AL
  69.   POPR AX,BX,CX,DX,DI
  70.   ;Записать получившийся модуль на диск
  71.   MOV AX,4000H
  72.   INT 21H
  73.   ;Ошибки не обрабатываем т.к. программа проста
  74. ER:
  75.   ;Закрыть файл
  76.   MOV AX,3E00H
  77.   INT 21H
  78.   ;Нормальное завершение программы
  79.   MOV AX,4C00H
  80.   INT 21H
  81. LastByte:
  82. CODE ENDS
  83. END START
  84.  

  В качестве примера, реализующего структуру, описанную выше, приведена программа-скелет, способная стать основой для написания ваших программ:

Код (Text):
  1.  
  2. ;############################################################
  3. ;Пример "скелета" программы для записи в ПЗУ
  4. ;============================================================
  5. TITLE BiosPassword
  6. LENGTHROM    EQU 2000H       ;Размер ПЗУ в байтах (8192)
  7. CODE SEGMENT BYTE PUBLIC
  8. ASSUME CS:CODE,DS:CODE
  9. ORG 0
  10. START:
  11.   DB 55h
  12.   DB 0AAh
  13.   ;Размер ПЗУ по модулю 200H
  14.   DB LENGTHROM SHR 9
  15.   ;Первая выполняемая команда
  16.   JMP BEGIN
  17. ;--------------------------------- Данные ----
  18.   CP1     DB 14,'Copyright (C) '
  19.   CP2     DB 18,'by Bordachev A.Y. '
  20.   CP3     DB 18,'ver. 1.00-93/03/31'
  21.   UNFACE  DB 4,':-( '
  22.   FACE    DB 4,';-) '
  23.   BIOS    DB 5,'BIOS '
  24.   PROMPT  DB 9,'PASSWORD:'
  25.   ERR1    DB 4,'ERR1'
  26. ;----------------------- Начало программы ----
  27. BEGIN:
  28.   ;Не забудьте настроить и другие регистры если необходимо
  29.   MOV AX,CS
  30.   MOV DS,AX
  31. ;---------------------------------------------
  32. ;- Здесь располагается код Вашей программы. -
  33. ;---------------------------------------------
  34.   ;Вернуть управление вызвавшей программе (BIOS)
  35.   RETF
  36.   ;Сюда запишем дополняющий байт
  37.   DB (0)
  38. CodeEnd:
  39.   ;Все что ниже, заполним кодом 0FFH
  40.   ;так как в чистом ПЗУ обычно содержится
  41.   ;именно этот код.
  42.   DB (LENGTHROM-(OFFSET CodeEnd-OFFSET START)) DUP (0FFH)
  43. LastByte:
  44. CODE ENDS
  45. END START
  46.  
© Андрей Бордачев

0 1.827
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532