关于WMI ACPI,建议先通读:1.<[原创]BIOS知识点滴Follow Bini系列之---WMI ACPI>了解WMI ACPI能提供什么后,再参考MSDN 2.<Windows Instrumentation: WMI and ACPI>,另外,3.<文件系统驱动编程基础篇之4——Wmi管理规范 mof文件>也可以作为1.的补充。需要特别说明的,1.中留了demo程序,一般读者没有环境测试(也不建议用真实机器测试,万一不开机损失挺大的),这时倒可以参考我的文章<解密OEM Bios导出给Windows的接口----导出OEM内部使用的WMI接口>,从现有BIOS中提取MOF和WMI接口用于学习。

本文应该是<解密OEM Bios导出给Windows的接口----导出OEM内部使用的WMI接口>一文的原理补充和总结,主要补充一下MOF资源文件和ACPI中各个WMI接口的关系。要厘清他们的关系,得先用<[原创]BIOS知识点滴Follow Bini系列之---WMI ACPI>文中使用的代码片:

Mof描述文件:

// Author: bini.Yi易祝兵 http://www.ufoit.com 2008-09-24
// File: demowmi.mof
//{39142400-C6A3-40fa-BADB-8A2652834100}
//IMPLEMENT_OLECREATE
//0x39142400, 0xc6a3, 0x40fa, 0xba, 0xdb, 0x8a, 0x26, 0x52, 0x83, 0x41, 0x00);[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;
};

ASL(WMI)文件:

// Author: bini.Yi易祝兵 2008-09-24
// File: demowmi.ASLDevice(DWMI)
{// PNP0C14 is PNP ID assigned to WMI mapperName(_HID, EISAID("PNP0C14"))Name(_UID, 0x0)Name(_WDG, Buffer(){// {39142400-C6A3-40fa-BADB-8A2652834100}0x00, 0x24, 0x14, 0x39, 0xA3, 0xC6, 0xFA, 0x40, 0xBA, 0xDB, 0x8A, 0x26, 0x52, 0x83, 0x41, 0x00, //GUID0x30, 0x30, //'00' Object ID0x01, //Instance Num0x01,        // 00 = Demo})Name(DD00, 0)Method(WQ00, 1){DBGS("Demo Wmi Get Function:")DW2H(DD00)Return(DD00)}Method(WS00, 2){DBGS("Demo Wmi Set Function:")DW2H(ARG1)Store(ARG1, DD00)}
}

WMI ACPI调用模型:

MOF描述文件是最终导出给用户调用的接口,WMI ACPI中的ASL code部分则是MOF所描述的Class的实现。如果读者反复阅读和对比MOF/ASL文件,会觉得他们很像动态链接库:MOF文件如同Class头文件,负责约定和导出调用接口。ASL如同DLL文件负责DLL函数的实现。至于_WDG对象,则充当了DLL的EAT(函数地址表)的中间人角色(其实还依赖于_WDG对象中嵌入式MOF文件,后面会提到),当然EAT表中可能会有若干表项组成,_WDG对象也是如此。_WDG中单个表项具有如下数据结构:

typedef struct _Mapper
{GUID guid;             // GUID that names data blockunion{CHAR ObjectId[2];  // 2-character ACPI ID  (Data Blocks and Methods)struct {UCHAR NotificationValue;  // Byte value passed by event handler control methodUCHAR Reserved[1];} NotifyId;}USHORT InstanceCount;  // Number of separate instances of data blockUSHORT Flags;          // Flags
}Mapper;

当所有表项结合在一起,形成一个巨大的Mapper[N]数组,这个数组最终构成_WDG对象。而Mof资源文件的编写者(无疑是OEM厂商了),会为_WDG对象中每个Mapper[i]制作Class。每个Class都有Mapper[i].guid对应。

调用过程(纯个人臆测,不过我觉得挺有道理的!):

  1. WMI的调用者(想象成DLL的调用者),根据MOF资源文件(想象成Class头文件)描述的WMI接口,以ClassName的形式去调用ASL code的(想象成DLL文件)时,ACPI.sys会到ACPI命名空间中先查找_WDG对象。

  2. 找到_WDG对象后,遍历其Mapper[N]数组,并定位到Mapper[i].Flags==0的数组项,该数组项指向一块buffer,buffer中包含编译后的MOF文件,MSDN称之为嵌入式MOF资源。

  3. ACPI.sys从嵌入式Mof资源中查找匹配的ClassName(因为上层使用Class调用WMI)。大家不妨回忆前面Mof资源描述文件中含有guid和ClassName

  4. 获得ClassName后,进一步可以获得接口名的guid,然后回到_WDG的Mapper[N]数组中,根据Mapper[i].guid查找与调用者GUID相匹配的数组项。

  5. 匹配到GUID后,ACPI.sys会取出Mapper[i].ObjectID成员,成员中包含xx值,ACPI.sys将WQ/WS和xx拼接,形成WQxx或者WSxx,然后去ACPI命名空间调用相应的WQxx或者WSxx方法。

    <[原创]BIOS知识点滴Follow Bini系列之---WMI ACPI>作为一个demo程序,_WDG对象的Mapper[N]数组只有一个数组项,没有体现上述搜索过程。所以,我换一个N>1的例子,嗯,那就用从<解密OEM Bios导出给Windows的接口----导出OEM内部使用的WMI接口>提取出来的ThinkPad的_WDG对象:

        Device (WMI2){Name (_HID, EisaId ("PNP0C14") /* Windows Management Instrumentation Device */)  // _HID: Hardware IDName (_UID, 0x02)  // _UID: Unique IDName (_WDG, Buffer (0x64){//==============Mapper[0]==============0xF1, 0x24, 0xB4, 0xFC, 0x5A, 0x07, 0x0E, 0x4E,0xBF, 0xC4, 0x62, 0xF3, 0xE7, 0x17, 0x71, 0xFA,0x41, 0x37,0x01,0x01,//==============Mapper[1]==============0xE3, 0x5E, 0xBE, 0xE2, 0xDA, 0x42, 0xDB, 0x49, 0x83, 0x78, 0x1F, 0x52, 0x47, 0x38, 0x82, 0x02,0x41, 0x38,0x01,0x02,//==============Mapper[2]==============0x9A, 0x01, 0x30, 0x74, 0xE9, 0xDC, 0x48, 0x45,0xBA, 0xB0, 0x9F, 0xDE, 0x09, 0x35, 0xCA, 0xFF, 0x41, 0x39,0x0A,0x05,//==============Mapper[3]==============0x03, 0x70, 0xF4, 0x7F, 0x6C, 0x3B, 0x5E, 0x4E,0xA2, 0x27, 0xE9, 0x79, 0x82, 0x4A, 0x85, 0xD1,0x41, 0x41,0x01,0x06,//==============Mapper[4]==============0x21, 0x12, 0x90, 0x05, 0x66, 0xD5, 0xD1, 0x11,0xB2, 0xF0, 0x00, 0xA0, 0xC9, 0x06, 0x29, 0x10, 0x42, 0x42,0x01,0x00                           })

这是ThinkPad T460部分_WDG对象,其中有5个数组项,最后是一个特殊数组项,暂不讨论。其他几个数组项对应的WMI接口如下:

On Error Resume NextSet 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 enumSeta.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 enumSeta.WriteLine("    InstanceName=" & instance.InstanceName)
next 'instanceRem Lenovo_PlatformSetting - Platform Setting
Set enumSet = Service.InstancesOf ("Lenovo_PlatformSetting")
a.WriteLine("Lenovo_PlatformSetting")
for each instance in enumSeta.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 enumSeta.WriteLine("    InstanceName=" & instance.InstanceName)
next 'instancea.Close
Wscript.Echo "lenvon Test Completed, see lenvon.log for details"

由于ThinkPad用的是嵌入式MOF,我无法获得原始的Mof文件(如同demowmi.mof),但是运行这段vbs脚本,可以推测出mof可能的内容:

//entry 1
[WMI,
Dynamic,
Provider("WmiProv"),
Locale("MS\\0x409"),
Description("Platform Setting"),
guid("{0x9A, 0x01, 0x30, 0x74, 0xE9, 0xDC, 0x48, 0x45,0xBA, 0xB0, 0x9F, 0xDE, 0x09, 0x35, 0xCA, 0xFF}")
]
class Lenovo_PlatformSetting
{[key, read]string InstanceName;[read] boolean Active;[WmiDataId(0x0A),read, write,Description("platfrom setting")] String CurrentSetting;
};//entry 2
[WMI,
Dynamic,
Provider("WmiProv"),
Locale("MS\\0x409"),
Description("Platform Setting"),
guid("{0x03, 0x70, 0xF4, 0x7F, 0x6C, 0x3B, 0x5E, 0x4E,0xA2, 0x27, 0xE9, 0x79, 0x82, 0x4A, 0x85, 0xD1}")
]
class Lenovo_SetPlatformSetting
{[key, read]string InstanceName;[read] boolean Active;[WmiMethodId,Description("Set platfrom setting")]
}//entry 3
[WMI,
Dynamic,
Provider("WmiProv"),
Locale("MS\\0x409"),
Description("Platform Setting"),
guid("{0xF1, 0x24, 0xB4, 0xFC, 0x5A, 0x07, 0x0E, 0x4E,0xBF, 0xC4, 0x62, 0xF3, 0xE7, 0x17, 0x71, 0xFA}")
]
class Lenovo_PreloadLanguage
{[key, read]string InstanceName;[read] boolean Active;[WmiDataId(0x01),read, write,Description("Preload Langugage")] String CurrentSetting;
}//entry 4
[WMI,
Dynamic,
Provider("WmiProv"),
Locale("MS\\0x409"),
Description("Platform Setting"),
guid("{}")
]
class Lenovo_SetPreloadLanguage
{[key, read]string InstanceName;[read] boolean Active;[WmiMethodId,Description("Set Set Preload Language")]
}

VBS中的InstanceOf:

借由Wmi Code generate生成的ACPI WMI接口测试脚本中有不少InstanceOf\instance语句:

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

(以下为个人理解:)前面说过Mof资源文件为_WDG对象的每个数组项编辑一个Class。套用面向对象思维,必须创建类对象(实例化Class)才能访问其成员,所以vbs脚本中

Set enumSet = Service.InstancesOf ("Lenovo_PlatformSetting")

应该就是创建Mof资源文件中描述的Lenovo_PlatformSetting类对象,并命名为enumSet。

VBS中"for each instance in enumSet":

如果把enumSet理解为某个Class的对象,那么vbs脚本中的instance就是Mof资源文件中描述的Class的各个成员变量。需要注意的是:阅读vbs测试脚本时,我们发现它是以循环遍历的方式访问类对象的各个成员的,所以,我们应该认为类对象中成员变量的类型一致,因此可以构成一个数组。另外,特殊情况下,如果数组中只有一项,那么数组将简化为普通变量。为了与_WDG对象中的Mapper[N]数组区分,我们用Item数组表示Class中类型一致的成员变量。Item数组的长度(即instance的数量或者说for循环的次数)在_WDG对象的Mapper[i].instanceCount中有指定:

//再次搬出这个结构!
typedef struct _Mapper
{GUID guid;             // GUID that names data blockunion{CHAR ObjectId[2];  // 2-character ACPI ID  (Data Blocks and Methods)struct {UCHAR NotificationValue;  // Byte value passed by event handler control methodUCHAR Reserved[1];} NotifyId;}USHORT InstanceCount;  // <----就是它指定instance的数量USHORT Flags;          // Flags
}Mapper;

以ThinkPad T460P为例:

Class:Lenovo_PlatformSetting.InstanceCount=0x0A;

Class:Lenovo_SetPlarformSeting.InstanceCount=0x01;

这些都可以在_WDG对象Mapper[i].InstanceCount中找到线索:

        Device (WMI2){Name (_HID, EisaId ("PNP0C14") /* Windows Management Instrumentation Device */)  // _HID: Hardware IDName (_UID, 0x02)  // _UID: Unique IDName (_WDG, Buffer (0x64){
...//==============Mapper[2]==============0x9A, 0x01, 0x30, 0x74, 0xE9, 0xDC, 0x48, 0x45,0xBA, 0xB0, 0x9F, 0xDE, 0x09, 0x35, 0xCA, 0xFF, 0x41, 0x39,0x0A, <-----  Lenovo_PlatformSetting.InstanceCount0x05,//==============Mapper[3]==============0x03, 0x70, 0xF4, 0x7F, 0x6C, 0x3B, 0x5E, 0x4E,0xA2, 0x27, 0xE9, 0x79, 0x82, 0x4A, 0x85, 0xD1,0x41, 0x41,0x01,0x06, <-----  Lenovo_PlarformSeting.InstanceCount=0x01

虽然,Item数组的长度随着Mapper[i].InstanceCount的值可以固定下来,但是Item数组的数组项类型却没有指定。简单的可能是UINT类型的Item数组,复杂的可能是Buffer或者Package类型的Item(再次注意,我这里用的是Item数组)。仍以ThinkPad T460P Lenovo_PlarformSeting类为例,Lenovo_PlarformSeting类函数为Lenovo_PlarformSeting,在WMI ACPI中的实现为:Method (WQA9, 1, NotSerialized)

Method (WQA9, 1, NotSerialized)
{Acquire (\_SB.WMI1.MWMI, 0xFFFF)If ((\WMIS (0x09, Arg0) != 0x00)){Release (\_SB.WMI1.MWMI)Return ("")}Local0 = DerefOf (ITEM [\WITM])Local1 = DerefOf (Local0 [0x00])Local2 = DerefOf (Local0 [0x01])Concatenate (Local2, ",", Local6)Local3 = DerefOf (VSEL [Local1])Concatenate (Local6, DerefOf (Local3 [\WSEL]), Local7)Release (\_SB.WMI1.MWMI)Return (Local7)
}

Method WQA9根据唯一的参数Arg0,从ACPI命名空间ITEM对象中获取数值。至于ITEM对象,它长这样

Name (ITEM, Package (0x06)
{Package (0x02){0x00, "InhibitEnteringThinkPadSetup"}, Package (0x02){0x00, "MTMSerialConcatenation"}, Package (0x02){0x00, "SwapProductName"}, Package (0x02){0x00, "ComputraceMsgDisable"}, Package (0x02){0x00, "CpuDebugEnable"}, Package (0x02){0x00, "PasswordAfterBootDeviceList"}
})

额,这个结构有点复杂,看不懂!我们慢慢梳理以下:

1.根据ACPI语法,Package可以认为结构体,也可以认为是数组。把最内层的Package (0x2)当作一个只有2个成员变量的结构体:

typedef struct _SettingEntry
{UINT32 SettingVal;char*  SettingName;
}SettingEntry;

外层Package理所当然可以认为是这样的结构体类型的Item数组,数组项共6项(虽然_WDG指定该数组项应该共有10项,但实际只有6项,而且访问超出部分也没发生异常!):

SettingEntry T460P[] = {{0x00,"InhibitEnteringThinkPadSetup"},{0x00,"MTMSerialConcatenation"},{0x00,"SwapProductName"},{0x00,"ComputraceMsgDisable"},{0x00,"CpuDebugEnable"},{0x00,"PasswordAfterBootDeviceList"}};

回到我们的标题:VBS中"for each instance in enumSet",vbs每轮循环,其实就是访问T460P[n]。对于有些OEM厂商会进行复杂的访问,比如仅仅访问T460P[n].SettingVal或者T460P[n].SettingName。这时就会在ASL code中看到大量的DerefOf语句。

_WDG中特殊的Flag:

每个_WDG对象Mapper[N]数组中都有一个(唯一的一个)特殊的数组项,它具有特殊的Flags值,其值为0x00。这个特殊的Mapper[i]数组项只有一个作用:提供buffer,存储编译后的Mof资源文件,因此这个表项本身并不会出现在Mof资源描述文件中。但它是构成调用ACPI WMI接口过程中重要的一步,没有它,无法实现Class名到ACPI Method的转换。

ACPI WMI 杂记相关推荐

  1. Linux命令:dmesg

    Linux命令:dmesg 功能说明:显示开机信息. 语 法:dmesg [-cn][-s <缓冲区大小>] 补充说明:kernel会将开机信息存储在ring buffer中.您若是开机时 ...

  2. ubuntu启动时的初始化信息一

    如果想知道Linux在启动时是如何加载硬件的,那就用dmesg去看看吧,从下面的代码中你可以清晰的看到ubuntu的初始化信息,从中学到很多的东西. [        0.000000] Initia ...

  3. rsyslogd 重启_RE: 服务器定时重启

    服务器定时重启,系统版本:CentOS release 6.2 (Final),内核:2.6.32-279.el6.x86_64 系统产生的日志如下: Jun 14 10:40:20 localhos ...

  4. linux开机dracut界面_CentOS启动报错dracut Warning: Boot has failed的解决方法

    CentOS无法启动,启动分区无法找到,然后就报了个堆栈信息: ACPI: wmi: Mapper loaded dracut Warning: No root device "block: ...

  5. 解密OEM Bios导出给Windows的接口----导出OEM内部使用的WMI接口

    Bios由于需要保证运行时的透明性,它只向提供该BIOS的OEM厂商开放部分接口.用户只能通过OEM提供的App/Driver读取和设置OEM Bios.另外,OEM也保留WMI接口用于设置BIOS, ...

  6. [原创]商城系统下单库存管控系列杂记(二)(并发安全和性能部分延伸)

      商城系统下单库存管控系列杂记(二)(并发安全和性能部分延伸)     前言   参与过几个中小型商城系统的开发,随着时间的增长,以及对系统的深入研究和测试,发现确实有很多值得推敲和商榷的地方(总有 ...

  7. .NET 框架中的 WMI 命名空间

    .NET 框架中的 WMI 命名空间   .NET框架中与WMI规范有关的命名空间有两个,分别是System.Management和System.Management.Instrumentation两 ...

  8. WMI Series :管理对象的信息查询和方法访问

    管理对象的信息查询和方法访问   在这一节内容,我们将通过几个实例来学习如何查询管理对象信息和访问管理对象提供的方法,这一部分内容将使用到我们在前面讲述到的System.Management命名空间中 ...

  9. WMI Series :事件预订和处理

    WMI事件概述 对于从事Winows编程的开发人员来说,事件驱动的应用程序设计是再熟悉不过了,但是WMI中的事件又是一个什么样的概念呢?对于宝贵的内存和CPU资源,管理员需要不断的监视其性能:对于磁盘 ...

最新文章

  1. 数据库收缩数据文件的尝试(二)(r11笔记第9天)
  2. 函数平移口诀_呆哥数学函数合集——函数的图形变换来啦【4】
  3. windows7 设置 Local Settings权限为可以访问
  4. wds+mdt 分布式自动部署 操作系统
  5. 【LeetCode】【HOT】17. 电话号码的字母组合(递归)
  6. 4周第4次课 压缩打包介绍 gzip bzip2 xz压缩工具
  7. 机器学习相关基本术语
  8. 怎样快速生成一个动态二维码?动态个性二维码怎么做?
  9. 123456789 中间随机添加 “加减符号” 进行运算结果等于100
  10. 钉钉获取用户信息 php,钉钉开发c#帮助类 获取用户信息 DingHelper.cs
  11. 【蓝桥杯练习--递归】费解的开关
  12. Java学习笔记(二)
  13. Go语言开发Windows应用
  14. matlab中axis square与axis equal区别
  15. C# webkit 内核浏览器 访问https网站 显示空白或者提示 Problem with the SSL CA cert (path? access rights?)
  16. msfvenom制作windows/linux/android/ios木马
  17. 大数据时代:SSAS从入门到放弃
  18. 6轴并联机器人开发--简介
  19. python自动化:实现自动回复QQ消息
  20. Informer--用于长序列时序预测【2021AAAI Best Paper】

热门文章

  1. 如何安全存储用户密码/数据库安全存储密码的方式
  2. IT创业见闻04-创业需具备最重要的资源是什么
  3. 部队军械仓库管理系统-军械库装备管理系统
  4. pwnable.kr - fd
  5. Android Jetpack
  6. Zookeeper——选举机制原理与Leader和Follower作用
  7. python 验证码识别 开源_Python 代码实现验证码识别
  8. 一个适合新手练习的单片机+安卓小项目(三)
  9. javaweb+jQuery ajax实例
  10. 整理一下在线免费SVN代码托管服务