Статья Форк софта CPUZ - скан бортового железа

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

1. Графическая оболочка GUI​
2. Процессор, BIOS, мат.плата​
3. Видео и ядро GPU​
4. Жёсткие диски ATA/SATA/NVM​
5. Система - тип и дата установки​
6. Заключение​



1. Графическая оболочка GUI

Для начала несколько слов об окне, куда будем выводить всю нарытую инфу.
Если в двух словах, то принцип прост до неприличия - мы создаём основное окно функцией CreateWindow(), а система на автомате будет обстреливать его своими оконными сообщениями WM_xx. Вроде всё понятно, но хотелось-бы остановиться на одном моменте - это тип самого окна. Известно, что окна в Windows бывают стандартными, и диалоговыми (модальные, немодальные, системные), а их программная реализация для взаимодействия с юзером отображена на рис.ниже:

DlgWin.webp

Как видим, при использовании диалоговых окон мы избавляемся как минимум от 8-ми функций Win32API, что упрощает непосредственную работу с окнами. Суть в том, что для создания обычного окна мы должны сначала создать его класс, далее зарегистрировать этот класс у оконного менеджера системы, и только потом создать рабочую область через CreateWindow(), которая кстати ожидает на входе аж 12 параметров. Более того, если наше окно предусматривает клавиатурный ввод, то потребуется ещё и непрерывный цикл обработки MsgLoop().

В общем для простых инфо-окон такая стратегия накладна и никак не оправдывает себя, а потому разумнее создать именно диалоговое окно всего одной функцией DialogBoxParam(). Тогда в качестве его родителя будет выступать рабочий стол "Desktop", а всю грязную работу возьмёт на себя сама система. Внутри оконной процедуры WndProc() будем обрабатывать поступающие сообщения, причём ОС будет постить в неё огромное кол-во типов мессаг WM_xx, а мы отсеим из них только необходимые. Как правило это WM_INITDIALOG(0x0110) в момент первоначального отображения окна, WM_COMMAND(0x0111) при клике на любых его элементах управления, и WM_CLOSE(0x0010) когда юзер закрывает форточку через пимпу(Х) системного меню в заголовке. В своей-же демке я добавил обработку и сообщения WM_TIMER(0x0103), чтобы в реальном времени показать ход счётчика тактов процессора "TimeStampCounter" TSC.

Оформление окна будем хранить в секции-ресурсов, где придётся описать детали каждого "элемента управления" вручную. По сути это только элементы типа "Static", в которые буду отправлять текст функцией SetDlgItemText() - вот её прототип:

C-подобный:
BOOL SetDlgItemTextA(
  [in] HWND   hDlg,        ;// хэндл диалогового окна
  [in] int    nIDDlgItem,  ;// ID элемента управления
  [in] LPCSTR lpString     ;// линк на текст для копирования
);

2. Процессор, BIOS, и мат.плата

Информацию о бортовом оборудовании можно найти в ветке системного реестра HKLM\System\CurrentControlSet\Enum, однако для доступа нужны админские права, да и сама инфа там довольно скудная, без важных деталей. Поэтому специализированный софт запрашивает паспорт напрямую у самих девайсов через их драйвера. Если в случае с ЦП достаточно вызвать инструкцию ассемблера CPUID с огромным зоопарком аргументов в регистре EAX, то с информацией о мат.плате BOARD не всё так просто, и придётся искать обходные пути.

К счастью производитель мат.плат прошивает в биос полные сведения о своём продукте, и прямо из под юзера мы можем прочитать их функцией GetSystemFirmwareTable(). Так получим сырую базу системного менежемента SMBIOS, которая содержит в себе макс 41 таблицу, хотя на практике вендор реализует не все из них. Однако в базе всегда будут присутствовать 4 таблицы с описанием ЦП, BIOS, BOARD и контроллёра памяти DDR-SDRAM.

Чтобы пропарсить эту базу в поисках перечисленных таблиц, придётся раскурить , последняя версия которой датируется августом 2024 года под номером v3.8. По объёму база достаточно большая (особенно на вирт.машинах), поэтому нужно предусмотреть приёмный буфер порядка 16 КБ. В скрепке к статье лежит исходник для сборки ассемблером fasm, где всё расписано до мелочей. В итоге получим такую картину:

cpu.webp

Здесь всё, кроме кодового имя ядра "Ivy Bridge", считано из соответствующих таблиц базы SMBIOS, а вот ядро вычисляется по коду CPUID. На данный момент в моей программе имеется список соответствий из 97 наименований процессоров Intel и AMD, которые я нашёл . Вполне возможно, что код не сможет определить имя ядра вашего процика, тогда просьба отписаться в комментах, и я добавлю его в свой список.


3. Видео и ядро графического процессора GPU

Единственно возможный вариант вытащить из системы детальную инфу об установленном видео-адаптере, это запросить её у подсистемы DirectX, но для начала нужно узнать имя девайса у менеджера конфигурации функцией CM_Get_Device_Interface_List(), далее открыть его D3DKMTOpenAdapterFromDeviceName(), и наконец вызвать с нужным параметром функцию D3DKMTQueryAdapterInfo(). Всё это получается довольно громоздко, но к сожалению других люков для доступа к видео не предусмотрено системой. Лично я сейчас сижу на встроенной видяхе чипсета H61 (внешняя GeForce-8800 внезапно предпочла умереть), а потому получил такой лог:

video.webp


4. Жёсткие диски ATA/SATA/NVM

Как и в случае с мат.платой, производитель хардов сохраняет паспорт своего продукта прямо на носителе, для чего выделяется специальный сектор размером в 512 байт. Для его чтения предусмотрена отдельная ATA-команда "IDENTIFY_DEVICE" с кодом 0xEC. Проблема в том, что это действительно только для носителей с интерфейсом ATA/SATA, а для дисков NVMe требуется уже другой подход, т.к. по сути это флэшки без блинов. Таким образом, если мы хотим получить информацию обо всех типах накопителей, нужно будет сначала определить интерфейс ATA/SATA или NVM, после чего выбрать один из двух вариантов чтения паспорта.

можно посылать драйверу накопителя функцией DeviceIoControl() с кодом "IOCTL_SMART_RCV_DRIVE_DATA". Обратите внимание, что для этого потребуются права админа. Если запрос вернёт ошибку, значит это диск NVMe и придётся вернуться, сменив код на стандартный "IOCTL_STORAGE_QUERY_PROPERTY", который возвращает значительно меньший объём информации, чем "IDENTIFY_DEVICE".

Раз уж имеем доступ к подноготной диска, ничего не мешает прочитать и его SMART командой "SMART_READ_DATA". Тогда получим такие сведения как: счётчик включений, кол-во проработанных часов, число переназначенных/сбойных секторов (если таковые имеются), и текущую температуру. Я недавно всего за 20$ приобрёл новый диск SATA "WesternDigital_Blue", и как показывает лог ниже, на данный момент включал его всего 46 раз, из которых он проработал 347 часов.

C-подобный:
;//....
          invoke  CreateFile,dName,GENERIC_READ or GENERIC_WRITE,\
                                   FILE_SHARE_READ,0,OPEN_EXISTING,0,0
          cmp     eax,-1
          jz      @f             ;// ошибка!
          mov     [hDisk],eax

;// ==== ATA-command "IDENTIFY-DEVICE (0xEC)"
          mov     [inBuff.irDriveRegs.CommandReg],IDENTIFY_DEVICE
          invoke  DeviceIoControl,eax,IOCTL_SMART_RCV_DRIVE_DATA,\
                                      inBuff,sizeof.SENDCMDINPARAMS,\
                                      outBuff,512+16,retSize,0
          or      eax,eax
          jz      @smartError    ;// если ошибка, значит диск NVMe
;//....

storage.webp


5. Система - тип и дата установки

В подвал окна я добавил инфу о самой системе - это её версия, время установки, и число проработанных часов с последнего включения. Версию можно взять из структуры РЕВ процесса, где прописаны сведения вплоть до сервис-пака "OSCSDVersion". Позаимствованные у WinDbg смещения от начала структуры представлены ниже. В данном случае имеем "Win 6.1.7601-SP1.0":

peb.webp

Время установки девственной системы может и хранится где-нибудь в реестре, однако можно заполучить его и более простым способом. Поскольку папка пользователей "Users" на диске C:\ создаётся именно в момент установки системы, то достаточно функцией FindFirstFile() прочитать время её создания, после чего чз FileTimeToSystemTime() привести его в читабельный вид. В свою очередь, время работы с последней загрузки в м/сек возвращает функция GetTickCount(), так-что простая арифметика "разделить на 60" вернёт нам время от дней, и вплоть до секунд.


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

В скрепку положил исходник для сборки fasm'ом, а так-же готовый исполняемый файл для тестов (запускать правой клавишей мыши от имени админа, иначе не сможем собрать инфу о жёстких дисках). Буду премного благодарен, если набросаете своих скринов, т.к. могут быть глюки, например отсутствие или неправильные значения в полях. Вот что представили мне коллеги на этапе тестирования кода, но этого явно мало:

HardInfo.webp


А здесь почему-то не определилось видео
screen_1.webp


У людей машины работают неделями без перебоя
screen_2.webp


Зверюга Ryzen-9, с 98 гигов памяти на борту
screen_3.webp


Походу чел геймер, и заточил машину под игры
screen_4.webp


Честно проработав ~40.000 часов, мой ATA-хард приказал долго жить
screen_5.webp
 

Вложения

Последнее редактирование:
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab