Note: Access SMBIOS in Kernel of Windows

因為老闆說要給部門做一個簡單的talk, 鑑於目前同事多數是 Junior level, 而公司從事的還是x86為主的硬體生意, 所以決定中秋過完的部門週會上, 簡單介紹一下SMBIOS 的來由與存取方法, 讀資料過程還是有新的收穫, 學到如何kernel 使用wmi

開始做一些研究, 找到一些舊的資料, 基本上SMBIOS 真的開始全面導入是 v2.3.1之後, Win95 就開始使用PNPBIOS 界面提供 SMBIOS的支援, 但是直到 1999年Microsoft 才開始加入 Design for Windows Logo 認證裡面(from PC 99 Design guide), 不過強制需求應該是從Windows XP SP2/Windows 2003 Server SP1 開始引入了mssmbios.sys, 在WinHEC 2005有Release了一個文件 smbios.doc

該文件解釋了mssmbios.sys的一些行為,  基本上是基於WMI機制等於內部做了MOF宣告, 然後會去parsing smbios table資料, 並且存在 registry path 下"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Mssmbios\Data"

因為基於WMI, 所以它提供XP SP2與之後版本的user/kernel mode 的 wmi 存取, 為了更簡便使用user mode 還透過增加GetSystemFirmwareTable() API (from Windows 2003 server sp1), 而kernel mode 則從Vista 開始 多了 AuxKlibGetSystemFirmwareTable

kernel wmi 則是透過 IoWmiOpenBlock(), IoWMIQueryAllData(), “SMBIOS_DATA_GUID" 去取得, 這還要看一下 driver load order, 否則貿然在kernel driver使用應該會出問題

廣告

Note: Some C# code snippets

最近工作內容牽扯 customization Windows Image, 或是需要緊急處理狀況, 都在用C sharp 多, 有時候發現內建.Net 有大量System libraries 還蠻方便的

installProductKey 這個實際上是參考 slmgr.vbs 去找出它如何 Install Product Key of Windows, 至於 getBaseBoardManufacturer 就是透過WMI去讀SMBIOS Data, 是有遇過BIOS 沒填好SMBIOS的狀況

static void installProductKey(string key)
{
    var mbs = new ManagementObjectSearcher("Select Version From SoftwareLicensingService");
            
    foreach (ManagementObject mo in mbs.Get())
    {
        mo.InvokeMethod("InstallProductKey", new object[] { key });
    }

}

static public string getBaseBoardManufacturer()
{
    string ret = "Unknow";
    try
    {
        var mbs = new ManagementObjectSearcher("Select Manufacturer From Win32_BaseBoard");
        foreach (ManagementObject mo in mbs.Get())
        {
            ret = mo["Manufacturer"].ToString();
        }
    }
    catch (Exception)
    {
        ret = "Unknow"; // Normally don't have SMBIOS information
    }
    return ret;
}

snippets on github

Note: using SetupAPI to find a special device on Windows system,

// EnumHIDDevice.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#pragma comment(lib, "setupapi")

bool chkDigitizer(void)
{
    HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
    bool bFind = false;
    hDevInfo = SetupDiGetClassDevs(
        &GUID_DEVCLASS_HIDCLASS,
        NULL,
        NULL,
        DIGCF_PRESENT);
#if 0
    // get special device instance
    hDevInfo = SetupDiGetClassDevs(
        &GUID_DEVCLASS_HIDCLASS,
        TEXT("HID\\VID_0458&PID_501E&COL05\\7&198536FE&0&0004"),
        NULL,
        DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
#endif

    if (INVALID_HANDLE_VALUE == hDevInfo)
        return false;

    SP_DEVINFO_DATA DeviceInfoData;
    ULONG DeviceIndex;

    ZeroMemory(&DeviceInfoData, sizeof(SP_DEVINFO_DATA));
    DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    DeviceIndex = 0;
    while (SetupDiEnumDeviceInfo(hDevInfo, DeviceIndex, &DeviceInfoData))
    {
        DeviceIndex++;

        TCHAR InstanceID[MAX_PATH + 1];
        DWORD reqSize = 0;
        TCHAR CheckLists[][MAX_PATH + 1] = {
            TEXT("HID\\VID_0458&PID_5020&COL05"), // new
            TEXT("HID\\VID_0458&PID_501E&COL05"), // older
        };

        SetupDiGetDeviceInstanceId(hDevInfo, &DeviceInfoData, InstanceID, sizeof(InstanceID), &reqSize);
        for (ULONG i = 0; i < 2; i++)
            if (NULL != _tcsstr(InstanceID, CheckLists[i]))
            {
                bFind = true;
                break;
            }
    }
    if (INVALID_HANDLE_VALUE != hDevInfo)
        SetupDiDestroyDeviceInfoList(hDevInfo);

    return bFind;
}

int _tmain(int argc, _TCHAR* argv[])
{

    if (chkDigitizer())
        _tprintf(TEXT("Find out a digitizer device on system\n"));
    else
        _tprintf(TEXT("no digitizer on system\n"));

	return 0;
}

Note: create a named EVENT for share with a correct access permission on Win 7 & later

最近遇到某個工作項目需要寫個Service 來與一般的應用程式互動, 本來想用Pipe or File mapping, 做IPC
但是實際開始工作後發現 他們之間透過Event 就可以處理,
但是Windows 從Vista 開始引入UAC後 開始加強系統各種Security(可以想成linux enabled SELinux)
對各種Windows 內的 Object 都有了權限要求, 因此建立Object實際要指定好權限
這篇簡單的SDDL 產生一個 kernel object 的權限片斷程式碼,
要觀察建立好的Kernel object 可以直接透過 Process Explorer開Low Panel並且指定Handle 這樣就可以

    wchar_t  myEventName[] = L"Global\\MyEventTest";
    SECURITY_ATTRIBUTES sa = { 0 };
    PSECURITY_DESCRIPTOR pSd = NULL;
    ConvertStringSecurityDescriptorToSecurityDescriptor(
	L"D:"			// Discretionary ACL
				// ref. http://msdn.microsoft.com/en-us/library/windows/desktop/ms686670(v=vs.85).aspx
	L"(A;;0x001F0003;;;BA)"	// EVENT_FULL_ACCESS, Built-in Administrators
	L"(A;;0x00100002;;;AU)",// SYNCHRONIZE & MODIFY_EVENT_STATE, Authenticated users
	SDDL_REVISION_1, &pSd, NULL);
	sa.nLength = sizeof(sa);
	sa.bInheritHandle = FALSE;
	sa.lpSecurityDescriptor = pSd;

	// create a auto-reset event for reload configuration
    m_hSignals[EVENT_RELOAD_CONF] = CreateEvent(&sa, FALSE, FALSE, myEventName);
    if (m_hSignals[EVENT_RELOAD_CONF] == NULL)
    {
	throw GetLastError();
    }
    LocalFree(pSd); // release 

ref. http://stackoverflow.com/questions/19049412/how-to-add-synchronisation-right-in-a-sddl-string-for-createevent