Статья GetSystemFirmwareTable() – чтение и разбор таблицы SMBIOS

Инженеры Microsoft тщательно оберегают свой системный периметр, жёстко пресекая попытки вторжения в ядерное пространство Win из вне. Анархия и вседозволенность закончилась с приходом ХР-SP3, когда юзеру был закрыт доступ к устройству \\Device\PhysicalMemory. До этого, в XP-SP2 и ниже, функцией NtOpenSection() можно было без проблем открыть любую область физической памяти как на чтение, так и на запись, со-всеми вытекающими последствиями. В результате, черви и прочая малварь чувствовала себя в ядре вольготно, поэтому мелкософт поспешил прикрыть эту лавочку. Таким образом, системе XP-SP3 выпала честь оказаться единственной, где юзеру был закрыт полный доступ к физ.памяти ОЗУ.

Однако корпорации строят операционные системы для потребителей, а им явно не понравилось такое решение. Так, чтобы не спровоцировать очередную "бурю в стакане", начиная с 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) представлен ниже:

RW_SMB.png


Не смотря, что спецификация SMBIOS детально расписывает поля всех структур, понять что-либо из их описания трудно – эта дока скорее для уже "прокаченных" юзеров. Зато как видим, в программе RW приводится дамп структуры, и ниже идёт информационная нагрузка каждого из его байтов. По логам этой утилиты мне удалось создать универсальный инклуд с описанием всех типов (см.скрепку). Теперь, проецируя структуры инклуда на дамп, можно выводить необходимую инфу на консоль.

В таблице ниже собраны все регламентированные спецификацией записи, хотя на практике список гораздо меньше и напрямую зависит от настроения разработчика биос. Например если в дампе мы встретим структуру под номером(6), значит она описывает характеристики модуля памяти DDR. Соответственно структура под номером(4) представляет собой паспорт центрального процессора и т.д.:


SMB_type.png



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 соответственно. Обратите внимание, что каждая из структур заканчивается терминальной парой нулей – именно на них мы будем ориентироваться, чтобы при разборе структур не вылетать за их границы:


HD_SMB.png


В качестве примера рассмотрим первую структуру типа(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_Enum.png


Если в двух словах, то мы можем запросить у системы три перечисленных типа таблиц – 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-байт:

SMB_log.png


А вот, что возвращает этот-же код на моём ноуте Win-10 с UEFI..
Как видим, область памяти в диапазоне С0000-FFFFFh уже не доступна, зато ACPI-таблиц вагон и целая тележка – аж 26 штук:


Enum_Win10.png



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'

SMB_datadump.png


Обратите внимание, что заполнение полей в структурах полностью лежит на совести разработчика BIOS/UEFI. Судя по этим логам, AMI (на правом скрине) оставляет большинство из полей пустыми, не забывая отмечать сей факт маркерами "To Be Filled By OEM" (должно быть заполнено производителем). Зато инженеры "Lenovo" на моём буке с UEFI стараются не пренебрегать деталями, по возможности заполняя все теги соответствующей информацией.

Как было сказано выше, здесь я упустил парсинг заголовков структур, где в закодированном виде хранятся более специфичные характеристики – в реальном софте это является обязательным условием, по аналогии с программой "RW-Utility". Именно для таких случаев я создал инклуд с описанием структур и прикрепил его в скрепке. Там-же найдёте два представленных выше исполняемых файла, если вдруг возникнет желание протестировать их на своей машине.


5. Заключение.

Согласно отчётам группы DMTF, на данный момент 2 млрд. клиентских и серверных операционных систем реализуют SMBIOS на своём борту. С 2000-года Microsoft начала требовать, чтобы производители материнских плат и поставщики биос поддерживали SMBIOS, для получения их сертификата. Это говорит о том, что программный интерфейс SMB активно развивается и сбрасывать его со-счетов не нужно. Особый интерес в этом направлении представляют ACPI-таблицы, но это тема заслуживает отдельной статьи и мы к ней как-нибудь вернёмся. А пока ставлю здесь точку, удачи и до скорого.
 

Вложения

  • SMBIOS.zip
    3,8 КБ · Просмотры: 507
Последнее редактирование:
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!