导语:HP笔记本驱动中发现keylogger,官方回应说是忘记删除调试代码,已发布更新来移除这些调试代码。 文末附技术分析。
概述
HP发布了上百个笔记本的驱动更新,来移除一些可以被攻击者用作keylogger组件的调试代码。
Keylogger的代码在HP笔记本的触摸板驱动文件SynTP.sys文件中。安全研究员ZwClose说,虽然默认在不开启的,但是可以通过设定注册表的值来开启keylogging。
注册表地址为:
HKLMSoftwareSynaptics%ProductName%HKLMSoftwareSynaptics%ProductName%Default
恶意软件可以用本地kernel签名的工具来开启这个注册表,记录用户的按键行为,安全产品也检测不到。恶意软件需要做的就是在更改注册表的时候绕过UAC,而目前可用的绕过UAC的方法超过10种。
回应
HP官方回应,keylogging代码是在调试阶段使用的,而发布时忘记删除了。受影响的笔记本一共有475个型号,包括303种家用笔记本和172种商用笔记本。受影响的主要型号有HP's 25*, mt**, 15*, OMEN, ENVY, Pavilion, Stream, ZBook, EliteBook, ProBook系列。具体见HP官方链接。
HP和Windows都发布了更新,可以删除调试代码。
技术分析
查看键盘驱动文件SynTP.sys,在IDA中打开:
这一行字符串格式有点像keylogger。字符串被sub_140022C10函数引用:
另一个字符串给了我关于sub_140022C10函数的暗示:CPalmDetect::KeyboardHookCallback。这是KeyboardHookCallback在反编译后进行工作的:
char __fastcall KeyboardHookCallback(unsigned int a1, int a2, unsigned int a3) { if ( DebugMask & 4 ) { LODWORD(v19) = v3; TraceMessage((__int64)"CPalmDetect::KeyboardHookCallback", 3u, "ulScanCode=0x%02X, bKeyFlags=%X", a1, v19); v4 = DebugMask; } ...
TraceMessage函数以调用WPP追踪函数结束:
void TraceMessage(char *pFuncName, unsigned char Level, const char *pFmt, ...) { char LogString[0x100]; va_list va; va_start(va, pFmt); if ( pFmt ) vsnprintf(LogString, 0xFFui64, pFmt, va); if ( WppDevObj != (WPP_TRACE_CONTROL_BLOCK *)&WppDevObj ) { if ( WppDevObj->Unk & 2 ) { if ( WppDevObj->Level >= Level ) WPP_SF_ss(WppDevObj->Logger, /*MessageNumber*/ 0xC, MessageGuid, pFuncName, LogString); } } }
用WmiTraceMessage 函数对 WPP_SF_ss进行格式化:
WPP_SF_ss(unsigned short LoggerHandle, unsigned short MessageNumber, LPGUID pMessageGuid, char *pFuncName, char *pLogString) { return WmiTraceMessage(LoggerHandle, /*MessageFlags*/ WPP_TRACE_OPTIONS, MessageGuid, MessageNumber, (pFuncName) ? strlen(pFuncName) + 1 : 5, (pFuncName) ? (pFuncName) : "NULL", (pLogString) ? strlen(pLogString) + 1 : 5, (pLogString) ? (pLogString) : "NULL", 0); }
如果设定了DebugMask的第2位,KeyboardHookCallback就调用TraceMessage传递参数“ulScanCode=0x%02X, bKeyFlags=%X”和scan代码到trace代码。TraceMessage的布局和DoTraceMessage宏很配。在Windows版本上,WPP tracing使用ETW或WMI机制。WPP_SF_ss是.tmh文件中自动生成的函数。第3个参数是MessageGuid,会再被传递给WmiTraceMessage。
另外一个很重要的细节是providerId,为了获取providerId必须要定位WPP_INIT_TRACE 宏的使用。因为该宏存储了WPP控制块结构的ControlGuid域的providerId指针。宏和providerId的使用:
__int64 __fastcall WPP_INIT_TRACING(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath) { ... RegisterAtExitFunctions(); WppMainCb.Callback = 0i64; WppMainCb.ControlGuid = (__int64)&ControlGuid; //ProviderId WppMainCb.Next = 0i64; WppMainCb.RegistryPath = 0i64; WppMainCb.FlagsLen = 1; WppMainCb.Level = 0; WppLoadTracingSupport(); WppMainCb.RegistryPath = 0i64; WppInitKm(pDrvObj, pRegPath); ... }
所以驱动的providerId 是{607781A3-0392-4422-87BC-C14CDEC63F9F},MessageId是{0F18D222C-D861-F023-2733-D780688CFBDF}。
如果DebugMask 的值是3,第二位没有设定,那么调试(keylogging)默认是不开启的。
对DebugMask XRefing发现了设定变量值的函数,我们叫它GetDebugMask:
//----- (000000014008A23C) ---------------------------------------------------- __int64 __fastcall GetDebugMask(_BYTE *a1) { result = GetDriverParameter(a1, (__int64)"Debug", (__int64)"Mask", (__int64)&v3); DebugMask = result; return result; } // 1400D0FA0: using guessed type int DebugMask;
GetDebugMask调用CRegistryBase::GetDriverParameter (sub_14002C588) 来从Windows注册表中读取DebugMask的值。GetDriverParameter的代码很冗长,足够说明他从下面的subkey中寻找第3个参数传来的变量名:
HKLM\Software\Synaptics\%ProductName% HKLM\Software\Synaptics\%ProductName%\Default
%ProductName% 可能是“SynTP”或“PointerPort”。值的类型是DWORD。