在本文中,我们将介绍今年添加到 Elastic Defend(从8.12 版开始)中的键盘记录器和键盘记录检测功能,该功能负责 Elastic Security 中的端点保护。本文也提供日语版本。
简介
从 Elastic Defend 8.12 开始,我们通过监视和记录键盘记录器使用的代表性 Windows API 调用,增强了对 Windows 上键盘记录器和具有键盘记录功能的恶意软件(例如信息窃取恶意软件或远程访问木马,俗称 RAT)的检测。本出版物将重点介绍此新功能的详细技术背景。此外,我们将介绍与此功能一起创建的新预构建行为检测规则。
什么是键盘记录器及其风险?
键盘记录器是一种监控和记录计算机上输入的击键的软件(※1)。虽然键盘记录器可用于用户监控等合法目的,但它们经常被恶意行为者滥用。具体来说,它们用于窃取敏感信息,例如身份验证凭据、信用卡详细信息以及通过键盘输入的各种机密数据。(※1:虽然有一些硬件键盘记录器可以通过 USB 直接连接到 PC,但本文重点介绍软件键盘记录器。)
通过键盘记录器获得的敏感信息可被用于盗取资金或作为进一步网络攻击的跳板。因此,尽管键盘记录本身不会直接损害计算机,但早期检测对于防止后续更具侵入性的网络攻击至关重要。
有许多类型的恶意软件具有键盘记录功能,尤其是 RAT、信息窃取程序和银行恶意软件。一些具有键盘记录功能的知名恶意软件包括Agent Tesla、LokiBot和SnakeKeylogger。
如何窃取击键?
接下来,让我们从技术角度解释键盘记录器如何在不被检测的情况下运行。虽然键盘记录器可以在各种操作系统环境(Windows/Linux/macOS 和移动设备)中使用,但本文将重点介绍 Windows 键盘记录器。具体来说,我们将描述四种不同的键盘记录器类型,它们使用 Windows API 和函数捕获击键(※2)。
顺便说一句,在这里解释键盘记录方法的原因是为了加深对本文后半部分介绍的新检测功能的理解。因此,提供的示例代码仅用于说明目的,不应按原样执行(※3)。
(※2:在 Windows 上运行的键盘记录器可以大致分为安装在内核空间(操作系统端)和安装在与常规应用程序相同空间(用户空间)的两种类型。本文重点介绍后一种类型。) (※3:如果根据以下提供的示例代码创建和滥用键盘记录器,Elastic 对任何后果概不负责。)
- 基于轮询的键盘记录器
这种类型的键盘记录器会轮询或定期检查键盘上每个键的状态(键是否按下),间隔时间很短(远小于一秒)。如果键盘记录器检测到自上次检查以来有新键被按下,它会记录和保存所按下键的信息。通过重复此过程,键盘记录器捕获用户输入的字符。
基于轮询的键盘记录器是使用检查键输入状态的 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
) 以捕获按键状态的方法不仅是一种众所周知的经典键盘记录技术,而且也是当今恶意软件常用的技术。
- 基于钩子的键盘记录器
与基于轮询的键盘记录器一样,基于钩子的键盘记录器也是一种存在了很长时间的经典类型。让我们首先解释一下什么是“钩子”。
钩子是一种机制,允许您将自定义处理(自定义代码)插入应用程序的特定操作中。使用钩子插入自定义处理称为“钩挂”。
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);
- 使用原始输入模型的键盘记录器
这种类型的键盘记录器捕获并记录直接从键盘等输入设备获得的原始输入数据。在深入探讨这种类型的键盘记录器的细节之前,必须了解 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
用于注册要从中接收原始输入数据的输入设备。在这里,它被设置为接收来自键盘的原始输入数据。
- 使用
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
新的键盘记录器端点检测规则
- 来自可疑进程的 GetAsyncKeyState API 调用
- 来自异常进程的 GetAsyncKeyState API 调用
- 通过 DirectInput 捕获按键输入
- 通过 RegisterRawInputDevices 捕获按键输入
- 通过 SetWindowsHookEx 钩住按键消息
- 从托管应用程序捕获按键输入
- 从可疑模块捕获按键输入
- 从可疑调用栈捕获按键输入
- 从未签名 DLL 捕获按键输入
- 通过 SetWindowsHookEx 捕获按键输入
凭借这套新的功能,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 的第一个参数。
当这些参数值指示尝试捕获键盘输入时,会发出警报。还可以查看 Kibana 中 RegisterRawInputDevices
API 的日志。
Windows API 调用期间收集的数据
由于篇幅限制,本文未涵盖所有添加的检测规则和 API 详细信息。但是,我们将简要描述 Elastic Defend 在调用相关 Windows API 期间收集的数据。有关每个项目的进一步说明,请参阅 custom_api.yml
中详细说明的 Elastic Common Schema (ECS) 映射。
API 名称 | 字段 | 描述 | 示例 |
---|---|---|---|
GetAsyncKeyState | process.Ext.api.metadata.ms_since_last_keyevent | 此参数指示上次 GetAsyncKeyState 事件之间的经过时间(以毫秒为单位)。 | 94 |
GetAsyncKeyState | process.Ext.api.metadata.background_callcount | 此参数指示所有 GetAsyncKeyState api 调用的数量,包括上次成功 GetAsyncKeyState 调用之间的不成功调用。 | 6021 |
SetWindowsHookEx | process.Ext.api.parameters.hook_type | 要安装的挂钩过程的类型。 | "WH_KEYBOARD_LL" |
SetWindowsHookEx | process.Ext.api.parameters.hook_module | 包含挂钩过程的 DLL。 | "c:\windows\system32\taskbar.dll" |
SetWindowsHookEx | process.Ext.api.parameters.procedure | 过程或函数的内存地址。 | 2431737462784 |
SetWindowsHookEx | process.Ext.api.metadata.procedure_symbol | 挂钩过程的摘要。 | "taskbar.dll" |
RegisterRawInputDevices | process.Ext.api.metadata.return_value | RegisterRawInputDevices API 调用的返回值。 | 1 |
RegisterRawInputDevices | process.Ext.api.parameters.usage_page | 此参数指示设备的顶级集合(使用页面)。RAWINPUTDEVICE 结构的第一个成员。 | "GENERIC" |
RegisterRawInputDevices | process.Ext.api.parameters.usage | 此参数指示使用页面内的特定设备(使用)。RAWINPUTDEVICE 结构的第二个成员。 | "KEYBOARD" |
RegisterRawInputDevices | process.Ext.api.parameters.flags | 模式标志,指定如何解释 UsagePage 和 Usage 提供的信息。RAWINPUTDEVICE 结构的第三个成员。 | "INPUTSINK" |
RegisterRawInputDevices | process.Ext.api.metadata.windows_count | 调用线程拥有的窗口数。 | 2 |
RegisterRawInputDevices | process.Ext.api.metadata.visible_windows_count | 调用线程拥有的可见窗口数。 | 0 |
RegisterRawInputDevices | process.Ext.api.metadata.thread_info_flags | 线程信息标志。 | 16 |
RegisterRawInputDevices | process.Ext.api.metadata.start_address_module | 与线程的起始地址关联的模块的名称。 | "C:\Windows\System32\DellTPad\ApMsgFwd.exe" |
RegisterRawInputDevices | process.Ext.api.metadata.start_address_allocation_protection | 与线程的起始地址关联的内存保护属性。 | "RCX" |
结论
在本文中,我们介绍了从 Elastic Defend 8.12 开始添加的 Windows 环境的键盘记录器和键盘记录检测功能。具体而言,通过监控与键盘记录相关的代表性 Windows API 的调用,我们集成了不依赖于签名的行为键盘记录检测方法。为了确保准确性和降低误报率,我们基于数月的研究创建了此功能和新规则。
除了与键盘记录相关的 API 之外,Elastic Defend 还监控 恶意行为者常用的其他 API(例如内存操作 API),提供多层保护。如果您对 Elastic Security 和 Elastic Defend 感兴趣,请查看 产品页面 和 文档。