天天看點

解密OEM Bios導出給Windows的接口----導出OEM内部使用的WMI接口

    Bios由于需要保證運作時的透明性,它隻向提供該BIOS的OEM廠商開放部分接口。使用者隻能通過OEM提供的App/Driver讀取和設定OEM Bios。另外,OEM也保留WMI接口用于設定BIOS,同樣,WMI接口也對使用者透明。雖然OEM企圖瞞天過海,但并不意味着我們無法擷取WMI接口。本文基于ThinkPad T460P提出一種獲得WMI接口的方式。另外,據了解,WMI接口可能存在安全風險,是以,這可能給滲透測試者提供了新的利用BIOS方式。

    Bios\Windows通過WMI互相通信需要借助MOF(經過編譯後為BMF)檔案,MOF描述了BIOS導出的WMI接口名字。按Windows Instrumentation: WMI and ACPI的描述,MOF可能存在于2處:

a.由HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WmiAcpi的MofImagePath指定;不過我在ThinkPad和某OEM的機器上沒有找到這樣的設定,是以這種方式不做讨論。

b.作為Buffer資源,将MOF資源嵌入到ACPI命名空間下;ThinkPad和某OEM都是用這種方式實作,當然這也是本文的切入點。

讀到這,你可能想到用RW獲得這個ACPI對象,但是聯想好像特意做了驅動,不讓使用者讀取ACPI相關記憶體,如下圖:

解密OEM Bios導出給Windows的接口----導出OEM内部使用的WMI接口

但是曆史規律告訴我們:真正的隔壁老王是堵不住!雖然Windows下看不到ACPI,但是,我可以U盤進入Ubuntu用pm-tools系列工具獲得需要的ACPI對象:

#acpidump > acpi.out ; dump所有的ACPI表,生成ASCII檔案
#acpixtract -a acpi.out ; 将acpidump輸出的ASCII檔案轉化為二進制.dat檔案(檔案中包含AML機器碼)
#iasl -d *.dat ;反編譯.dat檔案(AML),生成.dsl檔案(檔案中包含可讀的ASL源碼)
           

下圖是進過上述步驟,ThinkPad 460P的ACPI源碼檔案(ASL source code):

解密OEM Bios導出給Windows的接口----導出OEM内部使用的WMI接口

如《Windows Instrumentation: WMI and ACPI》所述,BIOS在ASL source code中需要向WMI mapper聲明裝置:PNP0c14和_WDG對象。另外,OEM廠商或者IBV會在聲明_WDG對象時,順帶聲明嵌入式MOF對象。ACPI spec沒有規定嵌入式MOF對象的名字,是以各家OEM會有不同的對象名。但是這并不是問題,它是有迹可循的:

a.嵌入式MOF對象位于_WDG對象附近,并且具有形如"Name (WQxy, Buffer()){"的ACPI對象定義(定義一個包含MOF資源的Buffer,并将該Buffer命名為WQxy。字首WQ代表這是WMI查詢接口,xy是2個16進制數值,用于辨別WMI查詢接口);

b.最重要的,該ACPI對象定義中包含了大量的16進制Byte,并且開頭4Byte是一個魔術字:FOMB,如下:

Name (WQBA, Buffer (0x089D)
            {
                /* 0000 */  0x46, 0x4F, 0x4D, 0x42, 0x01, 0x00, 0x00, 0x00,  // FOMB....
           

現在離獲得BIOS提供的WMI接口還有一步之遙,在繼續之前,需要交代一下MOF描述檔案生成MOF資源的過程。

#摘自:[原創]BIOS知識點滴Follow Bini系列之---WMI ACPI, 作者:bini
#http://www.ufoit.com/thread-420-1-1.html
#demowmi.mof

[WMI,
Dynamic,
Provider("WmiProv"),
Locale("MS\\0x409"),
Description("Events"),
guid("{39142400-C6A3-40fa-BADB-8A2652834100}")
]
class DemoWMIData
{
    [key, read]
     string InstanceName;
    [read] boolean Active;

    [WmiDataId(1),
     read, write,
     Description("description")
    ] uint32 Data;
};
           

上面是一個MOF描述檔案,經過mofcomp編譯生成二進制MOF資源檔案。OEM提取MOF資源檔案中的位元組流,嵌入到ACPI命名對象中。另外,DDK工具集中還提供wmimofck工具,以MOF資源檔案為輸入,生成MOF所描述的WMI接口的測試腳本。下面兩行指令将依次生成bmf檔案(即MOF資源檔案)和vbs測試腳本:

mofcomp -B:demowmi.bmf demowmi.mof
wmimofck -tdemowmi.vbs demowmi.bmf
           
解密OEM Bios導出給Windows的接口----導出OEM内部使用的WMI接口
解密OEM Bios導出給Windows的接口----導出OEM内部使用的WMI接口

VBS腳本中高亮處就是BIOS通過MOF導出的WMI接口。通過這段插曲,我是想說隻要有MOF資源檔案,就有機會解析出其中的WMI接口。另外,我們又知道ACPI表中包含MOF資源檔案,是以剩下的事就是生成MOF資源檔案和WMI測試腳本。剩下的篇幅我來談談怎麼生成MOF資源檔案。

1.将MOF資源中的位元組流轉換到二進制檔案中。我們看到的是儲存在ASL source code中的字元串,需要進過一系列轉換才能被wmimofck當做MOF資源檔案讀取,轉換過程其實和編寫shellcode相似。

a.用notepad++對字元串進行文本處理(如按住Alt選取要删除的列),左圖是ASL source code,右圖是轉換後的Opcode:

解密OEM Bios導出給Windows的接口----導出OEM内部使用的WMI接口

 --> 

解密OEM Bios導出給Windows的接口----導出OEM内部使用的WMI接口

b.為Opcode定義字元串數組,并生成可執行程式:

unsigned char arry[] = {"\x46\x4F\x4D\x42\x01\x00\x00\x00\
\x28\x05\x00\x00\xAE\x18\x00\x00\
\x44\x53\x00\x01\x1A\x7D\xDA\x54\
...
\x40\x2C\x27\x88\x80\xFC\xFF\x07"};

unsigned char arryEnd[] = {"\xaa\x55\xaa\x55"};

int main()
{
    printf("%s", arry);
    return 0;
}
           

c.IDA加載生成的可執行程式,搜尋并導出數組arry:

解密OEM Bios導出給Windows的接口----導出OEM内部使用的WMI接口

選中arry後,Edit--Export data--勾選raw bytes--Export,導出後的檔案即可作為MOF資源檔案。

解密OEM Bios導出給Windows的接口----導出OEM内部使用的WMI接口
解密OEM Bios導出給Windows的接口----導出OEM内部使用的WMI接口

有了bmf檔案,我們就可以用wmimofck生成vbs腳本,最後來看下ThinkPad T460P的WMI接口:

On Error Resume Next

Set fso = CreateObject("Scripting.FileSystemObject")
Set a = fso.CreateTextFile("lenvon.log", True)
Set Service = GetObject("winmgmts:{impersonationLevel=impersonate}!root/wmi")
Rem Lenovo_PreloadLanguage - Preload Language
Set enumSet = Service.InstancesOf ("Lenovo_PreloadLanguage")
a.WriteLine("Lenovo_PreloadLanguage")
for each instance in enumSet
    a.WriteLine("    InstanceName=" & instance.InstanceName)
    a.WriteLine("        instance.CurrentSetting=" & instance.CurrentSetting)
next 'instance

Rem Lenovo_SetPreloadLanguage - Set Preload Language
Set enumSet = Service.InstancesOf ("Lenovo_SetPreloadLanguage")
a.WriteLine("Lenovo_SetPreloadLanguage")
for each instance in enumSet
    a.WriteLine("    InstanceName=" & instance.InstanceName)
next 'instance

Rem Lenovo_PlatformSetting - Platform Setting
Set enumSet = Service.InstancesOf ("Lenovo_PlatformSetting")
a.WriteLine("Lenovo_PlatformSetting")
for each instance in enumSet
    a.WriteLine("    InstanceName=" & instance.InstanceName)
    a.WriteLine("        instance.CurrentSetting=" & instance.CurrentSetting)
next 'instance

Rem Lenovo_SetPlatformSetting - Set Platform Setting
Set enumSet = Service.InstancesOf ("Lenovo_SetPlatformSetting")
a.WriteLine("Lenovo_SetPlatformSetting")
for each instance in enumSet
    a.WriteLine("    InstanceName=" & instance.InstanceName)
next 'instance

a.Close
Wscript.Echo "lenvon Test Completed, see lenvon.log for details"
           

我們可以以管理者運作WMICodeCreator.exe,來驗證和執行這些WMI接口:Namespace選擇root\WMI,Classes可以選擇Lenovo_PlatformSetting\Lenovo_SetPlatformSetting等。

解密OEM Bios導出給Windows的接口----導出OEM内部使用的WMI接口

 參考:

1.BIOS知識點滴Follow Bini系列之---WMI ACPI

2.Windows Instrumentation: WMI and ACPI