导语: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中打开:

Strings.PNG

这一行字符串格式有点像keylogger。字符串被sub_140022C10函数引用:

sub_140022C10.PNG

另一个字符串给了我关于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追踪函数结束:

1512801217982661.png

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);
}

MessageGuid.PNG

如果设定了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的使用:

WPP_INIT_TRACING.PNG

__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);
...
}

ControlGuid.PNG

所以驱动的providerId 是{607781A3-0392-4422-87BC-C14CDEC63F9F},MessageId是{0F18D222C-D861-F023-2733-D780688CFBDF}。

如果DebugMask 的值是3,第二位没有设定,那么调试(keylogging)默认是不开启的。

DebugMask.PNG

对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。

源链接

Hacking more

...