Инженеры Microsoft тщательно оберегают свой системный периметр, жёстко пресекая попытки вторжения в ядерное пространство Win из вне. Анархия и вседозволенность закончилась с приходом ХР-SP3, когда юзеру был закрыт доступ к устройству \\Device\PhysicalMemory. До этого, в XP-SP2 и ниже, функцией NtOpenSection() можно было без проблем открыть любую область физической памяти как на чтение, так и на запись, со-всеми вытекающими последствиями. В результате, черви и прочая малварь чувствовала себя в ядре вольготно, поэтому мелкософт поспешил прикрыть эту лавочку. Таким образом, системе XP-SP3 выпала честь оказаться единственной, где юзеру был закрыт полный доступ к физ.памяти ОЗУ.
Однако корпорации строят операционные системы для потребителей, а им явно не понравилось такое решение. Так, чтобы не спровоцировать очередную "бурю в стакане", начиная с Win-Vista системный протекторат снизошёл до рядового программиста и в состав интерфейса Win32-API включил функцию GetSystemFirmwareTable(). Её возможности ограничены чтением самых верхних адресов физ.памяти в диапазоне от
-------------------------------------------------
Оглавление:
1. SMBIOS – общие сведения
Таблица "System Management BIOS" (SMB) хранится внутри системного BIOS/EFI, и при включении машины копируется в оперативную память компьютера. Она включает в себя характеристики буквально всех физических устройств материнской платы, начиная от процессора и заканчивая различными датчиками. Такой перенос данных избавляет ОС от необходимости проверять оборудование напрямую, чтобы определить перечень активных устройств. Из централизированной базы SMB черпают информацию такие утилиты прикладного уровня как WMI – Windows Management Instrumentation, что в дословном переводе означает "инструментарий управления Win".
Предшественником инфраструктуры SMBIOS был разработанный в далёком 1996-году интерфейс DMI-BIOS (Desktop Management Interface), который выкупила группа независимых разработчиков DMТF (Distributed Management Task Force) и похоронила его уже спустя 3-года. На смену DMIB, в преддверии ХХI-века они предложили удовлетворяющий современным требованиям х64 более продвинутый SMBIOS – последняя
Основную проблему при разборе этой базы представляет переменный размер её структур. Каждая из структур описывает отдельный девайс. Согласно спецификации, в таблице SMB зарезервировано место для 127 описателей, хотя на практике используются максимум 44. Производители материнских плат и биосов оставляют за собой право выбирать, характеристики каких именно устройств включать в эту таблицу. В результате, мы не можем предсказать состав и приходится обходить все имеющиеся структуры по номерам их типов "Type" (благо они строго регламентированы). Из всего знакомого мне софта, лишь программа
Не смотря, что спецификация SMBIOS детально расписывает поля всех структур, понять что-либо из их описания трудно – эта дока скорее для уже "прокаченных" юзеров. Зато как видим, в программе RW приводится дамп структуры, и ниже идёт информационная нагрузка каждого из его байтов. По логам этой утилиты мне удалось создать универсальный инклуд с описанием всех типов (см.скрепку). Теперь, проецируя структуры инклуда на дамп, можно выводить необходимую инфу на консоль.
В таблице ниже собраны все регламентированные спецификацией записи, хотя на практике список гораздо меньше и напрямую зависит от настроения разработчика биос. Например если в дампе мы встретим структуру под номером(6), значит она описывает характеристики модуля памяти DDR. Соответственно структура под номером(4) представляет собой паспорт центрального процессора и т.д.:
2. Формат информационных структур SMB
API-функция из библиотеки kernel32.dll под названием GetSystemFirmwareTable() возвращает в указанный буфер всю таблицу SMBIOS, если передать ей соответствующий аргумент. Повторюсь, что эта функция доступна только начиная с Win-Vista, т.е. на семёрке и выше. Приёмный буфер будет предварять не имеющая непосредственного отношения к таблице, 8-байтная структура "RawSMBiosData", где указывается версия SMB и размер скопированных данных. Вот как её описывает документация:
Далее, в буфере последовательно плечом-к-плечу следуют уже сами полезные структуры, которые начинаются с 4-байтного заголовка "Header", и заканчиваются терминальной парой нулей. Дескриптор структуры абсолютно не интересен – при разборе таблицы SMB ставку будем делать только на тип (см.перечень выше), и длину заголовка:
Как правило, помимо закодированных данных, в структурах имеются и текстовые строки. Суть в том, что если к началу структуры прибавить размер её заголовка, то мы в аккурат упрёмся в начало массива текстовых строк. Поскольку все строки нуль-терминальные, то с выводом их на консоль проблем не возникает. Как только встретим пару нулей, значит достигли конца структуры, и к ней сразу примыкает следующая.
Чтобы продемонстрировать это, я открыл полученный дамп в HEX-редакторе и выделил первые три структуры цветом. Здесь видно, что таблицу предваряет вспомогательная "RawSMBiosData" (см.серый блок на рис.ниже), которая прямым текстом сообщает нам версию 2.6 и размер данных 0х00000813 = 2067 байт. Далее следует зеленый блок, где в первом байте хранится тип структуры, а во-втором – размер её заголовка: 0x00 и 0x18 соответственно. Обратите внимание, что каждая из структур заканчивается терминальной парой нулей – именно на них мы будем ориентироваться, чтобы при разборе структур не вылетать за их границы:
В качестве примера рассмотрим первую структуру типа(0), которая описывает характеристики системного BIOS..
Судя по дампу ниже, текстовые строки начинаются со-смещения 18h, и это-же значение хранится в виде размера структуры во-втором байте от начала. Таким образом, если планируем вывести на консоль только строки, то для доступа к ним достаточно к началу структуры прибавить её размер. Данный алгоритм действителен для любой структуры, внутри таблицы SMBIOS:
3. EnumSystemFirmwareTables() – перечисление доступных в системе таблиц
Таблицу SMBIOS возвращает упомянутая ранее функция GetSystemFirmwareTable(). Но в наличии у системы имеется ещё одна база под названием "ACPI", в которой зарегистрированы все, способные вести диалог с питанием, устройства – Advanced Configuration & Power Interface. Этот интерфейс отвечает за настройку и поддержку например режима энергосбережения ноутбуков, спящего режима в десктопах, отключении питания жёстких дисков при простое, и т.д. Более того, в таблицах ACPI под названием "SLIC" и "MSDM", установленные в буках операционные системы Win-8,10 хранят лицензионные ключи, что представляет особый интерес.
В зависимости от реализации, в ACPI-базе могут находиться ~40 таблиц различного назначения. Однако функция GetSystemFirmwareTable() за один заход способна сохранить только одну из них, название которой мы передаём ей в аргументе. Если нужна вся база, то запрашивать придётся каждую из её таблиц, в цикле. Список всех поддерживаемых текущей операционной системой ACPI-таблиц возвращает функция EnumSystemFirmwareTables() с таким прототипом:
Если в двух словах, то мы можем запросить у системы три перечисленных типа таблиц – ACPI, SMBIOS и Firmfware, передав её имя в виде аргумента GetSystemFirmwareTable(). Прототип этой функции выглядит так:
Судя по
Как видим, в первом аргументе нужно передавать текст в привычном нам виде, а во-втором аргументе – почему-то задом-на-перёд. Что ими двигало - хз. Ну да ладно.. оставим это на их совести, а сами напишем код, который перечислит все доступные в системе базы "Firmware", с сохранением их в соответствующие файлы. Исходник подробно прокомментирован, а если возникнут вопросы, то их всегда можно задать в комментах статьи:
Из выхлопной трубы этого кода получим 4-файла с данными и сведениями о таблицах текущей мат.платы. На моём стационарном узле с Win7 и легаси-биосом на борту я получил следующий лог где видно, что область памяти
А вот, что возвращает этот-же код на моём ноуте Win-10 с UEFI..
Как видим, область памяти в диапазоне
4. Чтение и разбор данных SMBIOS
Но вернёмся к базе-данных SMBIOS, и в следующем примере попытаемся вывести на консоль текстовые строки всех доступных структур. Можно было разбирать поля каждой из записи вплоть до каждого байта, но здесь я ставил перед собой иную задачу – продемонстрировать циклический обход всех записей по номерам их типов 00-127.
Суть в том, что мы берём первый байт очередной структуры (номер типа) и проверяем его на 127. Это значение является маркером последней записи, так-что встретив его сразу отправляемся на выход. Иначе, к адресу начала прибавляем второй байт структуры "StructLength" и оказываемся в начале текстовых строк, которые и выводим на консоль во-внутреннем цикле, пока не встретим пару терминальных нулей. Функция printf() возвращает в
Обратите внимание, что заполнение полей в структурах полностью лежит на совести разработчика BIOS/UEFI. Судя по этим логам, AMI (на правом скрине) оставляет большинство из полей пустыми, не забывая отмечать сей факт маркерами "To Be Filled By OEM" (должно быть заполнено производителем). Зато инженеры "Lenovo" на моём буке с UEFI стараются не пренебрегать деталями, по возможности заполняя все теги соответствующей информацией.
Как было сказано выше, здесь я упустил парсинг заголовков структур, где в закодированном виде хранятся более специфичные характеристики – в реальном софте это является обязательным условием, по аналогии с программой "RW-Utility". Именно для таких случаев я создал инклуд с описанием структур и прикрепил его в скрепке. Там-же найдёте два представленных выше исполняемых файла, если вдруг возникнет желание протестировать их на своей машине.
5. Заключение.
Согласно отчётам группы DMTF, на данный момент 2 млрд. клиентских и серверных операционных систем реализуют SMBIOS на своём борту. С 2000-года Microsoft начала требовать, чтобы производители материнских плат и поставщики биос поддерживали SMBIOS, для получения их сертификата. Это говорит о том, что программный интерфейс SMB активно развивается и сбрасывать его со-счетов не нужно. Особый интерес в этом направлении представляют ACPI-таблицы, но это тема заслуживает отдельной статьи и мы к ней как-нибудь вернёмся. А пока ставлю здесь точку, удачи и до скорого.
Однако корпорации строят операционные системы для потребителей, а им явно не понравилось такое решение. Так, чтобы не спровоцировать очередную "бурю в стакане", начиная с Win-Vista системный протекторат снизошёл до рядового программиста и в состав интерфейса Win32-API включил функцию GetSystemFirmwareTable(). Её возможности ограничены чтением самых верхних адресов физ.памяти в диапазоне от
0хС0000
до 0хFFFFF
. В эту область попадают системные таблицы, такие как: биосы бортовых устройств загрузки (например встроенное видео), а так-же ACPI и SMB-структуры. Отметим, что объект \\Device\PhysicalMemory так и не вернулся обратно, и на данный момент у нас по-прежнему к нему нет доступа. В данной статье рассматриваются методы чтения "FirmwareTable", что она из-себя представляет и какую имеет практическую ценность.-------------------------------------------------
Оглавление:
1. SMBios – общие сведения;
2. Формат структур SMB;
3. Перечисление доступных таблиц – EnumSystemFirmwareTables();
4. Программное чтение и разбор данных SMBIOS;
5. Заключение.
1. SMBIOS – общие сведения
Таблица "System Management BIOS" (SMB) хранится внутри системного BIOS/EFI, и при включении машины копируется в оперативную память компьютера. Она включает в себя характеристики буквально всех физических устройств материнской платы, начиная от процессора и заканчивая различными датчиками. Такой перенос данных избавляет ОС от необходимости проверять оборудование напрямую, чтобы определить перечень активных устройств. Из централизированной базы SMB черпают информацию такие утилиты прикладного уровня как WMI – Windows Management Instrumentation, что в дословном переводе означает "инструментарий управления Win".
Предшественником инфраструктуры SMBIOS был разработанный в далёком 1996-году интерфейс DMI-BIOS (Desktop Management Interface), который выкупила группа независимых разработчиков DMТF (Distributed Management Task Force) и похоронила его уже спустя 3-года. На смену DMIB, в преддверии ХХI-века они предложили удовлетворяющий современным требованиям х64 более продвинутый SMBIOS – последняя
Ссылка скрыта от гостей
спецификации датирована июлем 2020-года, т.е. интерфейс активно развивается.Основную проблему при разборе этой базы представляет переменный размер её структур. Каждая из структур описывает отдельный девайс. Согласно спецификации, в таблице SMB зарезервировано место для 127 описателей, хотя на практике используются максимум 44. Производители материнских плат и биосов оставляют за собой право выбирать, характеристики каких именно устройств включать в эту таблицу. В результате, мы не можем предсказать состав и приходится обходить все имеющиеся структуры по номерам их типов "Type" (благо они строго регламентированы). Из всего знакомого мне софта, лишь программа
Ссылка скрыта от гостей
корректно парсит эту таблицу – её скрин с деталями структуры типа(0) представлен ниже:Не смотря, что спецификация SMBIOS детально расписывает поля всех структур, понять что-либо из их описания трудно – эта дока скорее для уже "прокаченных" юзеров. Зато как видим, в программе RW приводится дамп структуры, и ниже идёт информационная нагрузка каждого из его байтов. По логам этой утилиты мне удалось создать универсальный инклуд с описанием всех типов (см.скрепку). Теперь, проецируя структуры инклуда на дамп, можно выводить необходимую инфу на консоль.
В таблице ниже собраны все регламентированные спецификацией записи, хотя на практике список гораздо меньше и напрямую зависит от настроения разработчика биос. Например если в дампе мы встретим структуру под номером(6), значит она описывает характеристики модуля памяти DDR. Соответственно структура под номером(4) представляет собой паспорт центрального процессора и т.д.:
2. Формат информационных структур SMB
API-функция из библиотеки kernel32.dll под названием GetSystemFirmwareTable() возвращает в указанный буфер всю таблицу SMBIOS, если передать ей соответствующий аргумент. Повторюсь, что эта функция доступна только начиная с Win-Vista, т.е. на семёрке и выше. Приёмный буфер будет предварять не имеющая непосредственного отношения к таблице, 8-байтная структура "RawSMBiosData", где указывается версия SMB и размер скопированных данных. Вот как её описывает документация:
C-подобный:
struct RawSMBiosData
Method db 0 ;// ---> метод доступа (устарело)
MjVer db 0 ;// старшая часть версии SMB (мажор)
MnVer db 0 ;// младшая часть версии SMB (минор)
DmiRev db 0 ;// ---> версия DMI (устарело)
Length dd 0 ;// размер таблицы данных
ends
Далее, в буфере последовательно плечом-к-плечу следуют уже сами полезные структуры, которые начинаются с 4-байтного заголовка "Header", и заканчиваются терминальной парой нулей. Дескриптор структуры абсолютно не интересен – при разборе таблицы SMB ставку будем делать только на тип (см.перечень выше), и длину заголовка:
C-подобный:
struct HEADER
Type db 0 ;// Внимание!!! Тип очередной структуры
Length db 0 ;// размер заголовка с данными
Handle dw 0 ;// дескриптор структуры (не важен)
ends
Как правило, помимо закодированных данных, в структурах имеются и текстовые строки. Суть в том, что если к началу структуры прибавить размер её заголовка, то мы в аккурат упрёмся в начало массива текстовых строк. Поскольку все строки нуль-терминальные, то с выводом их на консоль проблем не возникает. Как только встретим пару нулей, значит достигли конца структуры, и к ней сразу примыкает следующая.
Чтобы продемонстрировать это, я открыл полученный дамп в HEX-редакторе и выделил первые три структуры цветом. Здесь видно, что таблицу предваряет вспомогательная "RawSMBiosData" (см.серый блок на рис.ниже), которая прямым текстом сообщает нам версию 2.6 и размер данных 0х00000813 = 2067 байт. Далее следует зеленый блок, где в первом байте хранится тип структуры, а во-втором – размер её заголовка: 0x00 и 0x18 соответственно. Обратите внимание, что каждая из структур заканчивается терминальной парой нулей – именно на них мы будем ориентироваться, чтобы при разборе структур не вылетать за их границы:
В качестве примера рассмотрим первую структуру типа(0), которая описывает характеристики системного BIOS..
Судя по дампу ниже, текстовые строки начинаются со-смещения 18h, и это-же значение хранится в виде размера структуры во-втором байте от начала. Таким образом, если планируем вывести на консоль только строки, то для доступа к ним достаточно к началу структуры прибавить её размер. Данный алгоритм действителен для любой структуры, внутри таблицы SMBIOS:
Код:
Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
----------------------------------------------------------
00000000 00 18 00 00 01 02 00 F0 03 0F 90 DE 8B 7F 01 00 .......р..ђЮ‹...
00000010 00 00 33 05 08 10 FF FF 41 6D 65 72 69 63 61 6E ..3...яяAmerican
00000020 20 4D 65 67 61 74 72 65 6E 64 73 20 49 6E 63 2E Megatrends Inc.
00000030 00 56 34 2E 34 00 31 31 2F 32 35 2F 32 30 30 39 .V4.4.11/25/2009
00000040 00 00 ..
struct TYPE0 ;//<------------ BIOS-info
Header SMBHEADER ;// заголовок первые 4-байта = тип и размер структуры, не считая строк
Reserved0 rb 2 ;// 0102 = резерв
SegAddr dw 0 ;// F000 = сегмент памяти BIOS (система копирует данные из флеш в F000:0000)
Reserved1 rb 1 ;// 03 = резерв
RomSize db 0 ;// 0F = закодированный размер Flash-BIOS = 1024 byte
BiosType dq 0 ;// 000000017F8BDE90 = закодированные характеристики BIOS (см.утилиту RW)
Ext1 db 0 ;// 33 = расширенные опции(1)
Ext2 db 0 ;// 05 = расширенные опции(2)
MajorRelease db 0 ;// 08 = старшая часть релиза
MinorRelease db 0 ;// 10 = младшая часть релиза (итого 8.16)
ECrelease dw 0 ;// FFFF = ещё один релиз (отсутствует)
String db ? ;// 41 = "А" = начало строк переменной длины
Terminal dw 0 ;// в хвосте – терминальная пара нулей!
ends
3. EnumSystemFirmwareTables() – перечисление доступных в системе таблиц
Таблицу SMBIOS возвращает упомянутая ранее функция GetSystemFirmwareTable(). Но в наличии у системы имеется ещё одна база под названием "ACPI", в которой зарегистрированы все, способные вести диалог с питанием, устройства – Advanced Configuration & Power Interface. Этот интерфейс отвечает за настройку и поддержку например режима энергосбережения ноутбуков, спящего режима в десктопах, отключении питания жёстких дисков при простое, и т.д. Более того, в таблицах ACPI под названием "SLIC" и "MSDM", установленные в буках операционные системы Win-8,10 хранят лицензионные ключи, что представляет особый интерес.
В зависимости от реализации, в ACPI-базе могут находиться ~40 таблиц различного назначения. Однако функция GetSystemFirmwareTable() за один заход способна сохранить только одну из них, название которой мы передаём ей в аргументе. Если нужна вся база, то запрашивать придётся каждую из её таблиц, в цикле. Список всех поддерживаемых текущей операционной системой ACPI-таблиц возвращает функция EnumSystemFirmwareTables() с таким прототипом:
C-подобный:
UINT EnumSystemFirmwareTables(
DWORD FirmwareTableProviderSignature, ;// тип требуемой таблицы: 'ACPI', 'RSMB', 'FIRM'
PVOID pFirmwareTableEnumBuffer, ;// указатель на приёмный буфер
DWORD BufferSize ;// размер приёмного буфера
);
;// Идентификаторы запрашиваемых типов таблиц.
;// Этот параметр может иметь одно из следующих значений:
;//------------------------------------------------------
"ACPI" Поставщик таблицы прошивки ACPI.
"RSMB" Поставщик таблицы Raw-SMBIOS.
"FIRM" Поставщик сырых прошивок. Не поддерживается для систем UEFI, вместо этого используйте RSMB.
Если в двух словах, то мы можем запросить у системы три перечисленных типа таблиц – ACPI, SMBIOS и Firmfware, передав её имя в виде аргумента GetSystemFirmwareTable(). Прототип этой функции выглядит так:
C-подобный:
UINT GetSystemFirmwareTable(
DWORD FirmwareTableProviderSignature, ;// тип таблицы: 'ACPI', 'RSMB', 'FIRM'
DWORD FirmwareTableID, ;// для RSMB=0, для FIRM=C0000h или E0000h, для ACPI='HPET' или прочие.
PVOID pFirmwareTableBuffer, ;// указатель на приёмный буфер (если нуль, возвратит требуемый размер буфера)
DWORD BufferSize ;// размер приёмного буфера.
);
Судя по
Ссылка скрыта от гостей
MSDN, помимо таблиц RSMB и ACPI, на устаревших системах без UEFI мы можем запросить и необработанные (сырые) данные типа "FIRM" – это два диапазона адресов: С0000-DFFFFh
и E0000-FFFFFh
. Во-втором дампе памяти Е0000h
можно будет обнаружить как таблицу ACPI, так и RSMB. То-есть функция сбрасывает область памяти "как она есть". Инженеры явно писали эту функцию не в трезвом уме, иначе как объяснить передачу аргументов в регистрах, причём в текстовом (4-символьном) виде, да ещё и с обратным порядком байт (Litle-Endian). Это-же надо было до такого додуматься! Например вот как выглядит запрос таблицы "FACP" из базы ACPI:
C-подобный:
invoke EnumSystemFirmwareTables, 'ACPI', pBuffer, 512
invoke GetSystemFirmwareTable, 'ACPI', 'PCAF', pBuffer, 256*1024
Как видим, в первом аргументе нужно передавать текст в привычном нам виде, а во-втором аргументе – почему-то задом-на-перёд. Что ими двигало - хз. Ну да ладно.. оставим это на их совести, а сами напишем код, который перечислит все доступные в системе базы "Firmware", с сохранением их в соответствующие файлы. Исходник подробно прокомментирован, а если возникнут вопросы, то их всегда можно задать в комментах статьи:
C-подобный:
format pe console
include 'win32ax.inc'
entry start
;//----------
.data
rName db 'RawSMBios.ROM',0 ;// имена выходных файлов
aName db 'ACPITable.ROM',0
cName db 'C000-DFFF.ROM',0
eName db 'E000-FFFF.ROM',0
fSize dd 0
fHndl dd 0
acpiTbl rb 128 ;// буфер для "EnumSystemFirmwareTables()"
text dd 0,0 ;// буфер под 4-символьную строку имени ACPI
align 16
buff rb 256*1024 ;// 256К-буфер под базу SMBIOS
;//----------
.code
start: invoke SetConsoleTitle,<'***Firmware Read v0.1***',0>
;//----- Запрос поддержки RSMB ------------------------------//
;// bswap преобразует имя поставщика в обратный порядок байт,
;// в регистр EAX вернётся размер сохранённых в буфере данных.
cinvoke printf,<10,' EnumSystemFirmwareTables() info',\
10,' ==================================',0>
mov eax,'RSMB'
bswap eax
invoke EnumSystemFirmwareTables,eax,buff,512
push eax
cinvoke printf,<10,' * RSMB return..: %u byte',10,\
' Enumeration:',0>,eax
pop ecx ;// размер данных
shr ecx,2 ;// разделить на 4 = длина цикла в двордах
mov esi,buff ;// адрес источника с данными
@@: lodsd ;// берём очередной элемент из буфера
or eax,eax ;// проверить его на нуль
je @firm ;// выйти из цикла, если равно
push esi ecx ;//
cinvoke printf,<' %X.',0>,eax ;// иначе: вывод элемента на консоль
pop ecx esi ;//
loop @b ;// промотать цикл ECX-раз..
;//----- Запрос поддержки FIRM -----------//
;// на системах с BIOS в буфере получим С0000 и Е0000,
;// на системах с UEFI в буфер ничего не возвращается
@firm: mov eax,'FIRM'
bswap eax
invoke EnumSystemFirmwareTables,eax,buff,512
push eax
cinvoke printf,<10,10,' * FIRM return..: %u byte',10,\
' Enumeration:',0>,eax
pop ecx
shr ecx,2
mov esi,buff
@@: lodsd
or eax,eax
je @acpi
push esi ecx
cinvoke printf,<' 0x%X.',0>,eax
pop ecx esi
loop @b
;//----- Запрос доступных таблиц ACPI -----//
;// в отличии от двух предыдущих, в буфер возвращаются текстовые строки
@acpi: mov eax,'ACPI'
bswap eax
invoke EnumSystemFirmwareTables,eax,buff,512
push eax
cinvoke printf,<10,10,' * ACPI return..: %u byte',10,\
' Enumeration: ',0>,eax
pop ecx ;// размер возвращённых данных
shr ecx,2 ;// /4 = длина цикла
mov esi,buff ;// источник для LODSD
mov edi,text ;// приёмник для STOSD
@@: lodsd ;// EAX = имя очередной таблицы ACPI (4-символа)
stosd ;// сохранить в приёмнике для вывода на консоль
push edi esi ecx
cinvoke printf,<'%s.',0>,text ;// напечать как строку
pop ecx esi edi
sub edi,4 ;// возвратить указатель приёмника на место
loop @b ;// промотать цикл ECX-раз..
@saveToFile:
;//===== Читаем данные и сбрасываем их в файлы ======================
;// Начинаем с регионов памяти С0000 и Е0000 запрашивая их отдельно.
cinvoke printf,<10,10,10,' GetSystemFirmwareTables() to file',\
10,' =================================',0>
mov eax,'FIRM'
bswap eax
mov ebx,0xC0000 ;// диапазон С0000-DFFFFh
invoke GetSystemFirmwareTable,eax,ebx,buff,256*1024
mov [fSize],eax ;// размер возвращённой инфы
mov esi,cName ;// указатель на имя выходного файла
stdcall WriteData ;// сбросить данные в файл!
cinvoke printf,<10,' Save FIRM-C0000: OK! %u byte',0>,[fSize]
mov eax,'FIRM'
bswap eax
mov ebx,0xE0000 ;// диапазон E0000-FFFFFh
invoke GetSystemFirmwareTable,eax,ebx,buff,256*1024
mov [fSize],eax
mov esi,eName
stdcall WriteData ;// сбросить данные в файл!
cinvoke printf,<10,' Save FIRM-E0000: OK! %u byte',0>,[fSize]
;//===== Запрашиваем базу Raw-SMBIOS ===============================
;// и так-же сохраняем её в файл ===================================
mov eax,'RSMB'
bswap eax
mov ebx,0
invoke GetSystemFirmwareTable,eax,ebx,buff,256*1024
mov [fSize],eax
mov esi,rName
stdcall WriteData
cinvoke printf,<10,' Save Raw-SMBios: OK! %u byte',0>,[fSize]
;//===== Запрашиваем базу ACPI с сохраненим в файл всех её таблиц =========
;// Для начала нужно получить список доступных таблиц,
;// после чего считывать их в цикле по одной ==============================
invoke _lcreat,aName,0 ;// создать общий для всех таблиц файл
mov [fHndl],eax ;// запомнить его дескриптор
mov [fSize],0 ;// очистить поле с размером
mov eax,'ACPI' ;// запрос на перечисление ACPI
bswap eax ;// восстановить порядок байт
invoke EnumSystemFirmwareTables,eax,acpiTbl,128
mov ecx,eax ;// ECX = размер возвращённых данных
shr ecx,2 ;// /4 = длина цикла (кол-во таблиц)
mov esi,acpiTbl ;// источник
@@: lodsd ;// EAX = имя очередной ACPI-таблицы
mov ebx,'ACPI' ;// EBX = первый аргумент (тип запроса)
bswap ebx ;//
push eax ebx ecx esi
invoke GetSystemFirmwareTable,ebx,eax,buff,256*1024
add [fSize],eax ;// суммируем размеры всех таблиц
invoke _lwrite,[fHndl],buff,eax ;// запись очередной таблицы в файл!
pop esi ecx ebx eax
loop @b ;// промотать цикл ECX-раз..
invoke _lclose,[fHndl] ;// прибить файл
cinvoke printf,<10,' Save ACPI table: OK! %u byte',0>,[fSize]
;//===== КОНЕЦ ПРОГРАММЫ ==================================
@exit: cinvoke gets,buff ;//
cinvoke exit,0 ;//
;//===== Вспомогательная процедура записи в файл ==========
;// принимает в ESI указатель на имя файла
proc WriteData
invoke _lcreat,esi,0 ;// создать файл
push eax ;// дескриптор
invoke _lwrite,eax,buff,[fSize] ;// записать в него данные
pop eax ;//
invoke _lclose,eax ;// закрыть файл
ret ;// выйти из процедуры
endp
;//----------
section '.idata' import data readable
library msvcrt,'msvcrt.dll',kernel32,'kernel32.dll'
import msvcrt, printf,'printf',gets,'gets',exit,'exit'
include 'api\kernel32.inc'
Из выхлопной трубы этого кода получим 4-файла с данными и сведениями о таблицах текущей мат.платы. На моём стационарном узле с Win7 и легаси-биосом на борту я получил следующий лог где видно, что область памяти
С0000-FFFFFh
доступна на чтение и в сумме составляет 262.144 байт. Биосом реализовано всего восемь ACPI-таблиц, которые код сдампил в файл размером 9.939-байт:А вот, что возвращает этот-же код на моём ноуте Win-10 с UEFI..
Как видим, область памяти в диапазоне
С0000-FFFFFh
уже не доступна, зато ACPI-таблиц вагон и целая тележка – аж 26 штук:4. Чтение и разбор данных SMBIOS
Но вернёмся к базе-данных SMBIOS, и в следующем примере попытаемся вывести на консоль текстовые строки всех доступных структур. Можно было разбирать поля каждой из записи вплоть до каждого байта, но здесь я ставил перед собой иную задачу – продемонстрировать циклический обход всех записей по номерам их типов 00-127.
Суть в том, что мы берём первый байт очередной структуры (номер типа) и проверяем его на 127. Это значение является маркером последней записи, так-что встретив его сразу отправляемся на выход. Иначе, к адресу начала прибавляем второй байт структуры "StructLength" и оказываемся в начале текстовых строк, которые и выводим на консоль во-внутреннем цикле, пока не встретим пару терминальных нулей. Функция printf() возвращает в
EAX
количество распечатанных байт – внутри одной структуры их будем использовать для смещения к следующей строке текста. Вот наглядный пример алгоритма:
C-подобный:
format pe console
include 'win32ax.inc'
include 'equates\smbios.inc' ;// подключаем инклуд SMBIOS (см.скрепку)
entry start
;//----------
.data
strucName dd type00,type01,type02,type03,type04,type05,type06,type07 ;// таблица указателей имён
dd type08,type09,type10,type11,type12,type13,type14,type15
dd type16,type17,type18,type19,type20,type21,type22,type23
dd type24,type25,type26,type27,type28,type29,type30,type31
dd type32,type33,type34,type35,type36,type37,type38,type39
dd type40,type41,type42,type33,type44
type00 db 10,10,'TYPE00 - BIOS Information:',0
type01 db 10,10,'TYPE01 - System Info:',0
type02 db 10,10,'TYPE02 - Baseboard:',0
type03 db 10,10,'TYPE03 - Chassis:',0
type04 db 10,10,'TYPE04 - Processor:',0
type05 db 10,10,'TYPE05 - Memory Controller:',0
type06 db 10,10,'TYPE06 - Memory Module:',0
type07 db 10,10,'TYPE07 - Cache Information:',0
type08 db 10,10,'TYPE08 - Port Connector:',0
type09 db 10,10,'TYPE09 - System Slots:',0
type10 db 10,10,'TYPE10 - On Board Devices:',0
type11 db 10,10,'TYPE11 - OEM Strings:',0
type12 db 10,10,'TYPE12 - System Configuration Options:',0
type13 db 10,10,'TYPE13 - BIOS Language:',0
type14 db 10,10,'TYPE14 - Group Associations:',0
type15 db 10,10,'TYPE15 - System Event Log:',0
type16 db 10,10,'TYPE16 - Physical Memory Array:',0
type17 db 10,10,'TYPE17 - Memory Device:',0
type18 db 10,10,'TYPE18 - 32-Bit Memory Error:',0
type19 db 10,10,'TYPE19 - Memory Array Mapped Address:',0
type20 db 10,10,'TYPE20 - Memory Device Mapped Address:',0
type21 db 10,10,'TYPE21 - Built-in Pointing Device:',0
type22 db 10,10,'TYPE22 - Portable Battery:',0
type23 db 10,10,'TYPE23 - System Reset:',0
type24 db 10,10,'TYPE24 - Hardware Security:',0
type25 db 10,10,'TYPE25 - System Power Controls:',0
type26 db 10,10,'TYPE26 - Voltage Probe:',0
type27 db 10,10,'TYPE27 - Cooling Device:',0
type28 db 10,10,'TYPE28 - Temperature Probe:',0
type29 db 10,10,'TYPE29 - Electrical Current Probe:',0
type30 db 10,10,'TYPE30 - Out-of-Band Remote Access:',0
type31 db 10,10,'TYPE31 - Boot Integrity Services (BIS) Entry Point:',0
type32 db 10,10,'TYPE32 - System Boot Information:',0
type33 db 10,10,'TYPE33 - 64-Bit Memory Error:',0
type34 db 10,10,'TYPE34 - Management Device:',0
type35 db 10,10,'TYPE35 - Management Device Component:',0
type36 db 10,10,'TYPE36 - Management Device Threshold Data:',0
type37 db 10,10,'TYPE37 - Memory Channel:',0
type38 db 10,10,'TYPE38 - IPMI Device Information:',0
type39 db 10,10,'TYPE39 - System Power Supply:',0
type40 db 10,10,'TYPE40 - Additional Information:',0
type41 db 10,10,'TYPE41 - Onboard Devices Extended:',0
type42 db 10,10,'TYPE42 - Management Controller Host Interface:',0
type43 db 10,10,'TYPE43 - TPM Device:',0
type44 db 10,10,'TYPE44 - Processor Additional Information:',0
align 16
buff rb 256*1024
;//----------
.code
start: invoke SetConsoleTitle,<'***SMBIOS info v0.1***',0>
cinvoke printf,<10,' SMBIOS information',\
10,' **************************',0>
;//===== Читаем базу SMB в приёмный буфер
mov eax,'RSMB'
bswap eax
invoke GetSystemFirmwareTable,eax,0,buff,256*1024
;//===== Выводим версию SMBIOS из структуры "RawSMBIOSData"
mov esi,buff ;// адрес начала SMB-базы
movzx eax,[esi+RawSMBIOSData.MjVer] ;// версия мажор
movzx ebx,[esi+RawSMBIOSData.MnVer] ;// версия минор
mov ecx,[esi+RawSMBIOSData.Length] ;// размер базы
cinvoke printf,<10,' SMB version..: %d.%d',\
10,' Table size...: %u byte',10,0>,eax,ebx,ecx
mov esi,buff+8 ;// смещаемся в буфере к первой структуре
@printSMBdata: ;//
movzx eax,byte[esi] ;// EAX = номер/тип очередной структуры
cmp eax,127 ;// проверить на последнюю структуру
je @exit ;// если достигли конца..
shl eax,2 ;// иначе: делаем номер индексом в таблице имён
mov ebx,strucName ;// EBX = начало таблицы имён
mov eax,[ebx+eax] ;// вычислить смещение
push esi
cinvoke printf,<' %s',0>,eax ;// описание типа на консоль
pop esi
movzx ebx,byte[esi+1] ;// EBX = второй байт из структуры (размер заголовка)
add esi,ebx ;// получить адрес начала текстовых строк
@@: push esi
cinvoke printf,<10,' %s',0>,esi ;// вывести очередную строку на консоль
pop esi
add esi,eax ;// получить адрес следующей строки
dec esi ;// коррекция указателя
cmp byte[esi],0 ;// проверить на терминальную пару нулей
jne @b ;// продолжить, если нет
inc esi ;// иначе: смещаем указатель на сл.структуру
jmp @printSMBdata ;// прогнать цикл до типа(127)
@exit: cinvoke gets,buff ;// конец кода!!!
cinvoke exit,0
;//----------
section '.idata' import data readable
library msvcrt,'msvcrt.dll',kernel32,'kernel32.dll'
import msvcrt, printf,'printf',gets,'gets',exit,'exit'
include 'api\kernel32.inc'
Обратите внимание, что заполнение полей в структурах полностью лежит на совести разработчика BIOS/UEFI. Судя по этим логам, AMI (на правом скрине) оставляет большинство из полей пустыми, не забывая отмечать сей факт маркерами "To Be Filled By OEM" (должно быть заполнено производителем). Зато инженеры "Lenovo" на моём буке с UEFI стараются не пренебрегать деталями, по возможности заполняя все теги соответствующей информацией.
Как было сказано выше, здесь я упустил парсинг заголовков структур, где в закодированном виде хранятся более специфичные характеристики – в реальном софте это является обязательным условием, по аналогии с программой "RW-Utility". Именно для таких случаев я создал инклуд с описанием структур и прикрепил его в скрепке. Там-же найдёте два представленных выше исполняемых файла, если вдруг возникнет желание протестировать их на своей машине.
5. Заключение.
Согласно отчётам группы DMTF, на данный момент 2 млрд. клиентских и серверных операционных систем реализуют SMBIOS на своём борту. С 2000-года Microsoft начала требовать, чтобы производители материнских плат и поставщики биос поддерживали SMBIOS, для получения их сертификата. Это говорит о том, что программный интерфейс SMB активно развивается и сбрасывать его со-счетов не нужно. Особый интерес в этом направлении представляют ACPI-таблицы, но это тема заслуживает отдельной статьи и мы к ней как-нибудь вернёмся. А пока ставлю здесь точку, удачи и до скорого.
Вложения
Последнее редактирование: