最近在研究硬盘温度的获取。看来ata spec,疯狂的在网上查找资料,还有就是分析hwmonitor.exe(这个帮助最大)。刚开始的时候什么都不会,到什么地方获取数据呢。在之前获取dmi中,有在spec中看到有温度。但是这个一般的主板商是不会写温度数据到里面的。而wmi很多就是读取dmi数据的。所以在获取cpu温度等上面,都是读不出数据的。因此,首先打消这个念头。
在疯狂的在网上搜寻资料之后,得到了一些有用的东西,那就是温度是在硬盘的SMART属性中有保存的。到这里我们知道了,硬盘温度保存的地方。如何获取到这部分数据呢。是怎样的一个数据结构呢?真正的问题来了。用smart获取硬盘温度,你可以再codeproject中会找一个工程。实例就是如何获取smart信息。很详细。建议看看这部分代码。他是通过读取//./PhysicalDrive0(第一块硬盘,之后一次类推)。但是有时候根本获取不到详细的属性信息。特别是在多硬盘的机器上,很可能会出现获取数据不正确的问题,主要的原因就是sata硬盘的问题。在使用codeproject中的样例就获取不到了,到这里这能另寻它路了。没有别的办法只有看spec了。看这书,我是有点看晕了。只能求救于网络了。很幸运,找到新大陆了,可以通过wmi方式得到属性值。既然这样那就用wmitools看看吧。下面把网上找到的内容粘上给大家参考一下:
检测硬盘温度的工具或软件,都是通过 S.M.A.R.T来读取,对硬盘直接 DeviceIoControl 或者 WMI 都可以得到SMART值.SMART 数据存储于 WMI 中 ROOT/WMI/MSStorageDriver_ATAPISmartData 命名空间中,其中属性 'VendorSpecific' 包含有硬盘温度的数据,这是ATA标准定义的。可能你读出这些数据来会有些困惑,其实这是一个结构,第一个和第二个字节代表 SMART 版本信息,从第三个字节起定义 SMART 的属性,每个属性为12字节长,每个属性的第一字节为当前属性定义,0x09 定义已经使用的小时数, 0xc2 为温度属性,第五字节表示当前温度。结构如下
struct SmartAttriubtes
{
char attrib;
char flags;
char worst;
char normal;
char current;
char current1;
char current2;
char current3;
char current4;
char current5;
char current6;
char current7;
}
struct VendorSpecific
{
unsigned short version;
SmartAttriubtes smartattrib [1];
}
如笔者硬盘读取如下数值,有些就省略了 {0xa, 0x00, 0x09, 0x32, 0, 0x63, 0x63, 0xc2, 0x03, 0, 0, 0, 0, 0, 0xc2, 0x22, 0, 0x2e, 0x3b, 0x2e, 0, 0, 0, 0x05, 0, 0, ...};
排列如下
{
0xa, 0x00 版本信息
0x09, 0x32, 0, 0x63, 0x63, [0xc2, 0x03], 0, 0, 0, 0, 0 硬盘使用小时数 这里是 0x3c2 = 962 小时
0xc2, 0x22, 0, 0x2e, 0x3b, [0x2e], 0, 0, 0, 0x05, 0, 0 这是当前温度 数值为 0x2e = 46 度
}
win2000 不支持此 WMI 属性,只能通过 DeviceIoControl 得到,如何实现请看考附录 SMART 的OpenSource 连接
确实如上面说的,2000下面没有这个东西。但是用wmi方式同样和用codeproject中的样例一样会有相同的问题。这下真的让人很郁闷。
只有最后一招了,调试分析hwmonitor了(逆向,还好一点点还是会的)。下面是主要分析:
//获取当前系统上的存储设备信息
InBuffer:
0012EDC4 00 00 00 00 00 00 00 00 E0 ED 12 00 ....?
00414272 |> /57 push edi ; /pOverlapped
00414273 |. |8D5424 38 lea edx, dword ptr [esp+38] ; |
00414277 |. |52 push edx ; |pBytesReturned
00414278 |. |8B5424 1C mov edx, dword ptr [esp+1C] ; |
0041427C |. |68 00020000 push 200 ; |OutBufferSize = 200 (512.)
00414281 |. |8D4424 50 lea eax, dword ptr [esp+50] ; |
00414285 |. |50 push eax ; |OutBuffer
00414286 |. |6A 0C push 0C ; |InBufferSize = C (12.)
00414288 |. |8D4C24 4C lea ecx, dword ptr [esp+4C] ; |
0041428C |. |51 push ecx ; |InBuffer
0041428D |. |68 00142D00 push 2D1400 ; |IoControlCode = 2D1400(IOCTL_STORAGE_QUERY_PROPERTY)
00414292 |. |52 push edx ; |hDevice
00414293 |. |897C24 58 mov dword ptr [esp+58], edi ; |
00414297 |. |897C24 5C mov dword ptr [esp+5C], edi ; |
0041429B |. |FF15 BC034A00 call dword ptr [<&KERNEL32.DeviceIoCo>; /DeviceIoControl
//获取smart版本
00414365 |> /57 push edi ; /pOverlapped
00414366 |. 8D4C24 34 lea ecx, dword ptr [esp+34] ; |
0041436A |. 51 push ecx ; |pBytesReturned
0041436B |. 6A 18 push 18 ; |OutBufferSize = 18 (24.)
0041436D |. 33C0 xor eax, eax ; |
0041436F |. 8D5424 24 lea edx, dword ptr [esp+24] ; |
00414373 |. 52 push edx ; |OutBuffer
00414374 |. 57 push edi ; |InBufferSize
00414375 |. 57 push edi ; |InBuffer
00414376 |. 894424 30 mov dword ptr [esp+30], eax ; |
0041437A |. 894424 34 mov dword ptr [esp+34], eax ; |
0041437E |. 894424 38 mov dword ptr [esp+38], eax ; |
00414382 |. 894424 3C mov dword ptr [esp+3C], eax ; |
00414386 |. 894424 40 mov dword ptr [esp+40], eax ; |
0041438A |. 894424 44 mov dword ptr [esp+44], eax ; |
0041438E |. 8B4424 2C mov eax, dword ptr [esp+2C] ; |
00414392 |. 68 80400700 push 74080 ; |IoControlCode = SMART_GET_VERSION
00414397 |. 50 push eax ; |hDevice
00414398 |. 897C24 50 mov dword ptr [esp+50], edi ; |
0041439C |. FF15 BC034A00 call dword ptr [<&KERNEL32.DeviceIoCo>; /DeviceIoControl
0012ED6C 00000148 |hDevice = 00000148
0012ED70 00074080 |IoControlCode = SMART_GET_VERSION
0012ED74 00000000 |InBuffer = NULL
0012ED78 00000000 |InBufferSize = 0
0012ED7C 0012EDA4 |OutBuffer = 0012EDA4
0012ED80 00000018 |OutBufferSize = 18 (24.)
0012ED84 0012EDBC |pBytesReturned = 0012EDBC
0012ED88 00000000 /pOverlapped = NULL
.text:004143AC mov ecx, [esp+254h+var_238] ; GETVERSIONINPARAMS.fCapabilities
.text:004143B0 shr ecx, 2
.text:004143B3 and ecx, 1
.text:004143B6 mov [ebx+718h], ecx
.text:004143BC jmp short loc_4143C0
if(GETVERSIONINPARAMS.fCapabilities & CAP_SMART_CMD ) == CAP_SMART_CMD)
{
}
//发送smart命令
0012EDF0 00 00 00 00 D8 01 01 4F C2 A0 B0 00 00 00 00 00 ..ǘ企?°..
0012EE00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........
.text:00414195 mov [eax+6], dl
.text:00414198 mov edx, [esp+1Ch+lpOutBuffer]
.text:0041419C push edx ; lpOutBuffer
.text:0041419D push 20h ; nInBufferSize
.text:0041419F push eax ; lpInBuffer
.text:004141A0 mov [eax], esi
.text:004141A2 mov esi, [esp+28h+hObject]
.text:004141A6 or bl, 0FAh
.text:004141A9 push 7C084h ; dwIoControlCode SMART_SEND_DRIVE_COMMAND
.text:004141AE shl bl, 4
.text:004141B1 push esi ; hDevice
.text:004141B2 mov byte ptr [eax+4], 0D8h
.text:004141B6 mov byte ptr [eax+7], 4Fh
.text:004141BA mov byte ptr [eax+8], 0C2h
.text:004141BE mov [eax+9], bl
.text:004141C1 mov byte ptr [eax+0Ah], 0B0h
.text:004141C5 call ds:DeviceIoControl ; Sends a control code directly to a specified device driver,
.text:004141C5 ; causing the corre
//接受smart数据
0012EDF0 00 02 00 00 D0 01 01 4F C2 A0 B0 00 00 00 00 00 ?.ǐ企?°..
0012EE00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........
.text:004145B5 call _memset
.text:004145BA add esp, 0Ch
.text:004145BD push 0 ; lpOverlapped
.text:004145BF lea edx, [esp+288h+BytesReturned]
.text:004145C3 push edx ; lpBytesReturned
.text:004145C4 push 210h ; nOutBufferSize
.text:004145C9 lea eax, [esp+290h+Dst]
.text:004145CD push eax ; lpOutBuffer
.text:004145CE push 20h ; nInBufferSize
.text:004145D0 lea ecx, [esp+298h+InBuffer]
.text:004145D4 push ecx ; lpInBuffer
.text:004145D5 push 7C088h ; dwIoControlCode SMART_REV_DRIVE_DATA
.text:004145DA push ebp ; hDevice
.text:004145DB call ds:DeviceIoControl ; Sends a control code directly to a specified device driver,
.text:004145DB ; causing the corresponding dev
while(SetupDiEnumDeviceInterfaces())
{
//获取所需的缓存大小
if(SetupDiGetDeviceInterfaceDetail())
{
//获取所需的数据
if(SetupDiGetDeviceInterfaceDetail())
{
//打开设备,设备名为获取buffer+4
hFile = CreateFile();
if(hFile != INVAILD_VALUE_HANDLE)
{
CloseHanle(hFile);
}
}
}
}
上面的inbuffer内容,具体的要看ata spec了。
可以尝试的调试分析一下,你会知道,在获取smart之前是打开设备。分析中你会知道它不是打开//./PhysicalDrive0之类的设备,而是首先通过存储设备类,知道有哪些可用的存储设备,使用SetDixxxxxxx函数可以得到具体设备名。然后GetPowerState得到设备是否正在运行,以及通过smart版本是否支持smart命令。如果这些都成立的话,那么就可以通过send smart命令进行下面的操作了。这个存储设备类的guid是{0x53f56307,0xb6bf,0x11do,0x94,0xf2,0x00,0xa0,0xc9,0x1e,0xfb,0x8b}.
在得到了smart属性之后就是解析的问题的。每个smart属性都是0xc字节大小,这个上面有说的。属性type为0xC2就是温度属性了。在偏移为5的地方就是当前温度值。在到每个属性之前有0x12字节的数据(这个是什么我就不管了)。上面还有一点没有说到的就是设备属性的获取。可以得到vendorid,和products,这个我是用来表示硬盘名称的。
说的还不是很详细。就说到这里吧。大致东西都说到了。