Asuka Najakima

保护您的设备免受信息窃取

使用 Windows API 行为进行键盘记录器检测

Protecting your devices from information theft

在本文中,我们将介绍今年添加到 Elastic Defend(从 8.12 版本开始)的键盘记录器和键盘记录检测功能,该功能负责 Elastic Security 中的端点保护。本文还提供日语版本。

简介

从 Elastic Defend 8.12 开始,我们通过监控和记录键盘记录器使用的代表性 Windows API 调用,增强了对 Windows 上键盘记录器和具有键盘记录功能的恶意软件(例如信息窃取恶意软件或远程访问木马,更广为人知的名称是 RAT)的检测。本出版物将重点介绍此新功能的详细技术背景。此外,我们将介绍与此功能结合创建的新的预构建行为检测规则。

什么是键盘记录器以及它们的风险是什么?

键盘记录器是一种监视和记录计算机上输入的击键的软件(※1)。虽然键盘记录器可以用于用户监控等合法目的,但它们经常被恶意行为者滥用。具体来说,它们被用来窃取敏感信息,例如身份验证凭据、信用卡详细信息以及通过键盘输入的各种机密数据。(※1:虽然有可以直接通过 USB 连接到 PC 的硬件键盘记录器,但本文重点介绍软件键盘记录器。)

通过键盘记录器获得的敏感信息可以被用于货币盗窃或作为进一步网络攻击的垫脚石。因此,虽然键盘记录本身不会直接损坏计算机,但早期检测对于预防后续更具侵入性的网络攻击至关重要。

有许多具有键盘记录功能的恶意软件,尤其是 RAT、信息窃取程序和银行恶意软件。一些具有键盘记录功能的知名恶意软件包括 Agent TeslaLokiBotSnakeKeylogger

击键是如何被窃取的?

接下来,让我们从技术角度解释键盘记录器如何在不被检测到的情况下工作。虽然键盘记录器可以在各种操作系统环境(Windows/Linux/macOS 和移动设备)中使用,但本文将重点介绍 Windows 键盘记录器。具体来说,我们将描述四种不同的键盘记录器类型,它们使用 Windows API 和函数捕获击键(※2)。

顺便提一下,在此处解释键盘记录方法是为了加深对本文后半部分介绍的新检测功能的理解。因此,提供的示例代码仅用于说明目的,并非旨在按原样执行(※3)。

(※2:在 Windows 上运行的键盘记录器可以大致分为安装在内核空间(操作系统侧)的键盘记录器和安装在与常规应用程序(用户空间)相同的空间中的键盘记录器。本文重点介绍后一种类型。)(※3:如果基于下面提供的示例代码创建和滥用键盘记录器,Elastic 将不承担任何后果。)

  1. 基于轮询的键盘记录器

这种类型的键盘记录器以短时间间隔(远小于一秒)轮询或定期检查键盘上每个键的状态(是否按下)。如果键盘记录器检测到自上次检查以来已按下新键,它会记录并保存按下的键的信息。通过重复此过程,键盘记录器会捕获用户输入的字符。

基于轮询的键盘记录器是使用检查按键输入状态的 Windows API 实现的,GetAsyncKeyState API 是一个代表性示例。此 API 可以确定特定键当前是否被按下,以及自上次 API 调用以来是否已按下该键。下面是使用 GetAsyncKeyState API 的基于轮询的键盘记录器的简单示例

while(true)
{
    for (int key = 1; key <= 255; key++)
    {
        if (GetAsyncKeyState(key) & 0x01)
        {
            SaveTheKey(key, "log.txt");
        }
    }
    Sleep(50);
}

轮询(GetAsyncKeyState)捕获按键状态的方法不仅是一种众所周知的经典键盘记录技术,而且也是当今恶意软件常用的技术。

  1. 基于钩子的键盘记录器

基于钩子的键盘记录器,与基于轮询的键盘记录器一样,是一种存在已久的经典类型。让我们先解释一下什么是“钩子”。

钩子是一种允许您将自定义处理(自定义代码)插入应用程序特定操作的机制。使用钩子插入自定义处理称为“挂钩”。

Windows 提供了一种机制,允许您将消息(事件)(例如应用程序的按键输入)挂钩,并且可以通过 SetWindowsHookEx API 来利用它。下面是使用 SetWindowsHookEx API 的基于挂钩的键盘记录器的简单示例

HMODULE hHookLibrary = LoadLibraryW(L"hook.dll");
FARPROC hookFunc = GetProcAddress(hHookLibrary, "SaveTheKey");

HHOOK keyboardHook = NULL;
    
keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,
                (HOOKPROC)hookFunc,
                hHookLibrary,
                0);
  1. 使用原始输入模型的键盘记录器

这种类型的键盘记录器捕获并记录直接从键盘等输入设备获取的原始输入数据。在深入研究这种类型的键盘记录器的详细信息之前,了解 Windows 中的“原始输入模型”和“原始输入模型”至关重要。以下是对每种输入方法的解释

  • 原始输入模型:来自键盘等输入设备的数据在被传递到应用程序之前由操作系统处理。
  • 原始输入模型:来自输入设备的数据由应用程序直接接收,而无需操作系统进行任何中间处理。

最初,Windows 仅使用原始输入模型。但是,随着 Windows XP 的推出,添加了原始输入模型,这可能是由于输入设备的多样性日益增加。在原始输入模型中,RegisterRawInputDevices API 用于注册要直接从中接收原始数据的输入设备。随后,使用 GetRawInputData API 获取原始数据。

下面是使用原始输入模型和这些 API 的键盘记录器的简单示例

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{

    UINT dwSize = 0;
    RAWINPUT* buffer = NULL;

    switch (uMessage)
    {
    case WM_CREATE:
        RAWINPUTDEVICE rid;
        rid.usUsagePage = 0x01;  // HID_USAGE_PAGE_GENERIC
        rid.usUsage = 0x06;      // HID_USAGE_GENERIC_KEYBOARD
        rid.dwFlags = RIDEV_NOLEGACY | RIDEV_INPUTSINK;
        rid.hwndTarget = hWnd;
        RegisterRawInputDevices(&rid, 1, sizeof(rid));
        break;
    case WM_INPUT:
        GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));

        buffer = (RAWINPUT*)HeapAlloc(GetProcessHeap(), 0, dwSize);

        if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, &dwSize, sizeof(RAWINPUTHEADER)))
        {
            if (buffer->header.dwType == RIM_TYPEKEYBOARD)
            {
                SaveTheKey(buffer, "log.txt");
            }
        }
        HeapFree(GetProcessHeap(), 0, buffer);
        break;
    default:
        return DefWindowProc(hWnd, uMessage, wParam, lParam);
    }
    return 0;
}

在此示例中,RegisterRawInputDevices 用于注册要从中接收原始输入数据的输入设备。此处,它设置为从键盘接收原始输入数据。

  1. 使用 DirectInput 的键盘记录器

最后,让我们讨论一个使用 DirectInput 的键盘记录器。简单来说,此键盘记录器滥用 Microsoft DirectX 的功能。DirectX 是一组用于处理游戏和视频等多媒体任务的 API(库)。

由于从用户那里获取各种输入在游戏中至关重要,DirectX 也提供了处理用户输入的 API。DirectX 8 版本之前的 API 被称为 DirectInput。下面是一个使用相关 API 的简单键盘记录器示例。另外,当使用 DirectInput 获取按键状态时,后台会调用 RegisterRawInputDevices API。

LPDIRECTINPUT8		lpDI = NULL;
LPDIRECTINPUTDEVICE8	lpKeyboard = NULL;

BYTE key[256];
ZeroMemory(key, sizeof(key));

DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (LPVOID*)&lpDI, NULL);
lpDI->CreateDevice(GUID_SysKeyboard, &lpKeyboard, NULL);
lpKeyboard->SetDataFormat(&c_dfDIKeyboard);
lpKeyboard->SetCooperativeLevel(hwndMain, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE | DISCL_NOWINKEY);

while(true)
{
    HRESULT ret = lpKeyboard->GetDeviceState(sizeof(key), key);
    if (FAILED(ret)) {
        lpKeyboard->Acquire();
        lpKeyboard->GetDeviceState(sizeof(key), key);
    }
  SaveTheKey(key, "log.txt");	
    Sleep(50);
}

通过监控 Windows API 调用来检测键盘记录器

Elastic Defend 使用 Windows 事件跟踪 (ETW ※4) 来检测上述键盘记录器类型。这是通过监控对相关 Windows API 的调用并记录特别异常的行为来实现的。以下是被监控的 Windows API 以及与这些 API 相关的新创建的键盘记录器检测规则。(※4:简而言之,ETW 是 Microsoft 提供的一种机制,用于跟踪和记录 Windows 中应用程序和系统组件(如设备驱动程序)的执行情况。)

受监控的 Windows API

新的键盘记录器端点检测规则

凭借这套新的功能,Elastic Defend 可以全面监控和检测键盘记录活动,增强 Windows 端点针对这些威胁的安全性和保护。

检测 Windows 键盘记录器

接下来,让我们通过一个实际的检测示例。我们将使用 Elastic Defend 通过原始输入模型来检测键盘记录器。对于此示例,我们准备了一个名为 Keylogger.exe 的简单 PoC 键盘记录器,它使用 RegisterRawInputDevices API,并在我们的测试环境中执行了它 ※5。(※5:执行环境为 Windows 10 版本 22H2 19045.4412,这是撰写本文时可用的最新版本。)

  键盘记录器执行后不久,端点上触发了一个检测规则(通过 RegisterRawInputDevices 捕获击键输入),显示了一个警报。该警报的更多详细信息可以在 Kibana 中查看。

以下是检测规则的详细信息,请注意示例中引用的特定 API。

query = '''
api where
 process.Ext.api.name == "RegisterRawInputDevices" and not process.code_signature.status : "trusted" and
 process.Ext.api.parameters.usage : ("HID_USAGE_GENERIC_KEYBOARD", "KEYBOARD") and
 process.Ext.api.parameters.flags : "*INPUTSINK*" and process.thread.Ext.call_stack_summary : "?*" and
 process.thread.Ext.call_stack_final_user_module.hash.sha256 != null and process.executable != null and
 not process.thread.Ext.call_stack_final_user_module.path :
                         ("*\\program files*", "*\\windows\\system32\\*", "*\\windows\\syswow64\\*",
                          "*\\windows\\systemapps\\*",
                          "*\\users\\*\\appdata\\local\\*\\kumospace.exe",
                          "*\\users\\*\\appdata\\local\\microsoft\\teams\\current\\teams.exe") and 
 not process.executable : ("?:\\Program Files\\*.exe", "?:\\Program Files (x86)\\*.exe")
'''

当未签名进程或由不受信任的签名者签名的进程调用 RegisterRawInputDevices API 来捕获击键时,此规则会发出警报。更具体地说,Elastic Defend 监视传递给 RegisterRawInputDevices API 的参数,特别是 RAWINPUTDEVICE 结构的成员,这是此 API 的第一个参数。

当这些参数值表明试图捕获键盘输入时,会发出警报。RegisterRawInputDevices API 的日志也可以在 Kibana 中查看。

在 Windows API 调用期间收集的数据

由于篇幅限制,本文不涵盖所有添加的检测规则和 API 详细信息。但是,我们将简要介绍 Elastic Defend 在调用相关 Windows API 期间收集的数据。有关每个项目的进一步说明,请参阅 custom_api.yml 中详细说明的 Elastic Common Schema (ECS) 映射。

API 名称字段描述示例
GetAsyncKeyStateprocess.Ext.api.metadata.ms_since_last_keyevent此参数表示上次 GetAsyncKeyState 事件之间经过的时间,以毫秒为单位。94
GetAsyncKeyStateprocess.Ext.api.metadata.background_callcount此参数表示上次成功的 GetAsyncKeyState 调用之间,所有 GetAsyncKeyState api 调用(包括不成功的调用)的次数。6021
SetWindowsHookExprocess.Ext.api.parameters.hook_type要安装的钩子过程的类型。"WH_KEYBOARD_LL"
SetWindowsHookExprocess.Ext.api.parameters.hook_module包含钩子过程的 DLL。"c:\windows\system32\taskbar.dll"
SetWindowsHookExprocess.Ext.api.parameters.procedure过程或函数的内存地址。2431737462784
SetWindowsHookExprocess.Ext.api.metadata.procedure_symbol钩子过程的摘要。"taskbar.dll"
RegisterRawInputDevicesprocess.Ext.api.metadata.return_valueRegisterRawInputDevices API 调用的返回值。1
RegisterRawInputDevicesprocess.Ext.api.parameters.usage_page此参数表示设备的顶级集合(使用页面)。RAWINPUTDEVICE 结构的第一个成员。"GENERIC"
RegisterRawInputDevicesprocess.Ext.api.parameters.usage此参数表示使用页面中的特定设备(使用情况)。RAWINPUTDEVICE 结构的第二个成员。"KEYBOARD"
RegisterRawInputDevicesprocess.Ext.api.parameters.flags指定如何解释 UsagePage 和 Usage 提供的信息的模式标志。RAWINPUTDEVICE 结构的第三个成员。"INPUTSINK"
RegisterRawInputDevicesprocess.Ext.api.metadata.windows_count调用线程拥有的窗口数量。2
RegisterRawInputDevicesprocess.Ext.api.metadata.visible_windows_count调用线程拥有的可见窗口数量。0
RegisterRawInputDevicesprocess.Ext.api.metadata.thread_info_flags线程信息标志。16
RegisterRawInputDevicesprocess.Ext.api.metadata.start_address_module与线程起始地址关联的模块的名称。"C:\Windows\System32\DellTPad\ApMsgFwd.exe"
RegisterRawInputDevicesprocess.Ext.api.metadata.start_address_allocation_protection与线程起始地址关联的内存保护属性。"RCX"

结论

在本文中,我们介绍了从 Elastic Defend 8.12 开始添加的 Windows 环境的键盘记录器和键盘记录检测功能。具体来说,通过监视对与键盘记录相关的代表性 Windows API 的调用,我们集成了一种不依赖于签名的行为键盘记录检测方法。为了确保准确性并降低误报率,我们根据数月的研究创建了此功能和新规则。

除了与键盘记录相关的 API 外,Elastic Defend 还监视恶意行为者常用的其他 API,例如用于内存操作的 API,从而提供多层保护。如果您对 Elastic Security 和 Elastic Defend 感兴趣,请查看产品页面文档