[心得] 關於SMBIOS的讀取方法
SMBIOS是一組記錄系統的組態資訊的表格所成集合。例如BIOS版號, EC版號, 有什麼插槽
之或是有幾根RAM的資訊。一般來說, BIOS會在開機時把SMBIOS的進入點寫到F000:0000~
FFFF之間的任意位址裡, 我們再寫個AP, 在F000:0000~FFFF之間找到'_SM_'這樣的字串來
確定進入點, 並讀取每張表格。不過, SMBIOS的specification並沒有要求每張表格要塞
在那小小的64K裡, 於是就產生了以下問題:
如果進入點和各張表格都塞在那64K裡, 那簡單。用真實模式的手法硬幹就可以讀到全部
的表格。(在Win32, 則因為DOS VM的關係, 會把前1MB的實體記憶體映射到16 bit行程裡,
所以也沒問題)。但若是各張表格沒有放在那64K裡, 情況就會變得很複雜。真實模式的手
法行不通了, 因為碰不到1MB以上的範圍! 在DOS下, 可以透過保護模式來讀, 但在Win32下
, 雖然身處保護模式, 但因為Virtual Memory的關係, 所以碰不到Physical Memory, 這時
候就只能寫個Driver, 把那塊存有SMBIOS的記憶體Map到行程裡。
只是讀個小小的記憶體要搞得這麼麻煩, 很令人頭痛對吧。不過Windows有提供一個API
, 叫GetSystemFirmwareTable, 它可以幫忙讀到SMBIOS, 但它只能用在64bit XP上頭, 所
以還是不方便。另一個方式則是透過WMI, 但是程式要寫一大堆, 也不方便。如果有更直接
的方式就好了。其實, Win32有個Driver叫mssmbios.sys, 它會在開機時把SMBIOS的值偷到
以下路徑:
"HKLM\\system\\currentcontrolset\\services\\mssmbios\\data"
所以我們只需要透過讀機碼就可以得到那一堆表格了, 很方便。以下是我寫的一個類別
, 它可以列印Type 0的內容...
#pragma comment(lib, "advapi32")
#include <windows.h>
#include <tchar.h>
#include <cstdio>
#include <cstdlib>
#include <vector>
using namespace std;
inline TCHAR const *DumpData(PBYTE dat, DWORD len)
{
static TCHAR ret[65536]=_T("");
TCHAR buf[65536]=_T("");
TCHAR txt[17]=_T(""), hex[53]=_T("
");
DWORD i=0, j=0;
buf[0]=_T('\n');
while(i<len)
{
txt[j]=dat[i]>=30?dat[i]:_T('.');
_stprintf(&hex[j*3], _T("%02X "), (BYTE)dat[i]); hex[(j+1)*3]=_T(' ');
j++;
if(j==16)
{
_tcscat(buf, _T("\n"));
_tcscat(buf, hex);
_tcscat(buf, txt);
j=0;
}
i++;
}
if(j>0)
{
_tcsnset(&hex[j*3], _T(' '), 52-j*3);
_tcsnset(&txt[j], _T(' '), 16-j);
_tcscat(buf, _T("\n"));
_tcscat(buf, hex);
_tcscat(buf, txt);
}
_tcscpy(ret, buf);
return ret;
}
class CSMBIOS
{
public:
typedef struct {PBYTE off; DWORD len;} table_t;
typedef vector<table_t> tables_t;
protected:
PBYTE m_pData;
tables_t m_idx[0x89];
public:
CSMBIOS(void);
~CSMBIOS();
tables_t GetTable(BYTE nType);
WORD GetSMVersion(void);
BYTE GetDMIRevision(void);
};
CSMBIOS::CSMBIOS(void) : m_pData(NULL)
{
HKEY key=0;
DWORD len=0;
LONG err=0;
err=RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"system\\currentcontrolset\\services\\mssmbios\\data", 0, KEY_READ, &key);
if(err==ERROR_SUCCESS)
{
err=RegQueryValueEx(key, "smbiosdata", NULL, NULL, NULL, &len);
if(err==ERROR_SUCCESS)
{
m_pData=(PBYTE)malloc(len);
if(m_pData)
{
err=RegQueryValueEx(key, "smbiosdata", NULL, NULL, m_pData, &len);
if(err==ERROR_SUCCESS)
{
for(PBYTE ptr=m_pData+8;ptr<m_pData+len;)
{
BYTE type=*ptr;
table_t tab={ptr, 0};
if(type>=0x89)
break;
for(ptr+=ptr[1];ptr<m_pData+len && (*ptr || ptr[1]);ptr++);
ptr+=2;
tab.len=ptr-tab.off;
m_idx[type].push_back(tab);
}
return;
}
free(m_pData);
RegCloseKey(key);
m_pData=NULL;
}
RegCloseKey(key);
}
RegCloseKey(key);
}
}
CSMBIOS::~CSMBIOS()
{ free(m_pData); }
CSMBIOS::tables_t CSMBIOS::GetTable(unsigned char nType)
{ return m_idx[nType]; }
WORD CSMBIOS::GetSMVersion(void)
{ return *(WORD *)(m_pData+1); }
BYTE CSMBIOS::GetDMIRevision(void)
{ return *(BYTE *)(m_pData+3); }
class CType0 : public CSMBIOS
{
public:
char const *m_pVendor;
char const *m_pBIOSVerStr;
WORD m_nBIOSAddr;
char const *m_pBIOSDateStr;
ULONG m_nROMSize;
__int64 m_nBIOSChar;
WORD m_nBIOSExtChar;
WORD m_nBIOSRev;
WORD m_nECRev;
CType0(void);
};
CType0::CType0(void) : CSMBIOS(),
m_pVendor(NULL), m_pBIOSVerStr(NULL), m_nBIOSAddr(0), m_pBIOSDateStr(NULL),
m_nROMSize(0),
m_nBIOSChar(0), m_nBIOSExtChar(0), m_nBIOSRev(0), m_nECRev(0)
{
tables_t tabs=GetTable(0);
if(!tabs.size())
return;
m_pVendor=(char const *)(tabs[0].off+tabs[0].off[1]);
m_pBIOSVerStr=m_pVendor+strlen(m_pVendor)+0x1;
m_nBIOSAddr=*(PWORD)(tabs[0].off+0x6);
m_pBIOSDateStr=m_pBIOSVerStr+strlen(m_pBIOSVerStr)+1;
m_nROMSize=*(PWORD)(tabs[0].off+0x9);
m_nBIOSChar=*(__int64 *)(tabs[0].off+0xa);
WORD ver=GetSMVersion();
ver=MAKEWORD(LOBYTE(ver), HIBYTE(ver));
if(ver>=0x0204)
{
m_nBIOSExtChar=*(PWORD)(tabs[0].off+0x12);
m_nBIOSRev=*(PWORD)(tabs[0].off+0x14);
m_nECRev=*(PWORD)(tabs[0].off+0x16);
}
}
int _tmain(int argc, TCHAR *argv[])
{
CType0 type0;
_tprintf(_T("SMBIOS version:\t%04x\n"), type0.GetSMVersion());
_tprintf(_T("Vendor:\t%s\n"), type0.m_pVendor);
_tprintf(_T("BIOS Version:\t%s\n"), type0.m_pBIOSVerStr);
_tprintf(_T("BIOS Date:\t%s\n"), type0.m_pBIOSDateStr);
_tprintf(_T("BIOS Address:\t%x\n"), type0.m_nBIOSAddr);
_tprintf(_T("ROM Size:\t%u\n"), type0.m_nROMSize);
_tprintf(_T("BIOS Characeristics:\t%x\n"), type0.m_nBIOSChar);
_tprintf(_T("BIOS Extension Characeristics:\t%x\n"), type0.m_nBIOSExtChar);
_tprintf(_T("BIOS Reversion:\t%x\n"), type0.m_nBIOSRev);
_tprintf(_T("EC Reversion:\t%x\n"), type0.m_nECRev);
return 0;
}
這樣的程式在網路上到處都有, 重點是怎麼把它寫得很漂亮。我是用個類別包起來, 不知
道大家覺得怎麼樣。
--
裸になって
何が悪い?
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 114.36.35.211
推
11/20 11:45, , 1F
11/20 11:45, 1F
推
11/20 11:46, , 2F
11/20 11:46, 2F
→
11/20 11:47, , 3F
11/20 11:47, 3F
→
11/20 11:48, , 4F
11/20 11:48, 4F
→
11/20 11:49, , 5F
11/20 11:49, 5F
→
11/20 11:50, , 6F
11/20 11:50, 6F
推
11/20 12:19, , 7F
11/20 12:19, 7F
推
11/20 12:48, , 8F
11/20 12:48, 8F
推
11/20 12:56, , 9F
11/20 12:56, 9F
推
11/20 16:32, , 10F
11/20 16:32, 10F
推
11/20 23:46, , 11F
11/20 23:46, 11F