Files
sigma-rules/rta/collection_keylog_rawinputdevice.py
T
Samirbous ec609d826a [New RTA] Input Capture via Keylog (#3033)
* [New RTA] Input Capture via Keylog

APIs in scope covered by 2 seperate RTAs  :

SetWindowsHookEx (collection_keylog_hook_keystate)
GetAsyncKeyState (collection_keylog_hook_keystate)
RegisterRawInputDevices (collection_keylog_rawinputdevice)

* Update rta/collection_keylog_hook_keystate.py

Co-authored-by: Mika Ayenson <Mikaayenson@users.noreply.github.com>

* Update rta/collection_keylog_rawinputdevice.py

Co-authored-by: Mika Ayenson <Mikaayenson@users.noreply.github.com>

---------

Co-authored-by: Mika Ayenson <Mikaayenson@users.noreply.github.com>
Co-authored-by: shashank-elastic <91139415+shashank-elastic@users.noreply.github.com>
2024-05-24 11:37:42 +01:00

275 lines
9.2 KiB
Python

# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License
# 2.0; you may not use this file except in compliance with the Elastic License
# 2.0.
# Adjusted version of https://github.com/XRoemer/Organon/blob/master/source/py/rawinputdata.py
from . import common
from . import RtaMetadata
import time, sys
metadata = RtaMetadata(
uuid="89f2b412-bbc7-4298-8768-2f3d3b43c93b",
platforms=["windows"],
endpoint=[
{"rule_name": "Keystroke Input Capture via DirectInput", "rule_id": "102b5c1a-7f2a-4254-8b26-6b299705fce7"},
{"rule_name": "Keystroke Input Capture via RegisterRawInputDevices", "rule_id": "4dbb9dfb-b3e2-49d7-8919-d6f221526df4"},
],
siem=[],
techniques=["T1056", "T1056.001"],
)
@common.requires_os(*metadata.platforms)
def main():
from ctypes import c_long, c_int, c_uint, c_ushort, Structure, Union
from ctypes import WINFUNCTYPE, windll, byref, sizeof, pointer, WinError
from ctypes.wintypes import DWORD, HWND, HANDLE, WPARAM, ULONG, LONG, UINT
from ctypes.wintypes import BYTE, LPCSTR, HINSTANCE, LPVOID
wndproc = WINFUNCTYPE(c_long, c_int, c_uint, c_int, c_int)
class WNDCLASS(Structure):
_fields_ = [('style', c_uint),
('lpfnWndProc', wndproc),
('cbClsExtra', c_int),
('cbWndExtra', c_int),
('hInstance', HINSTANCE),
('hIcon', HANDLE),
('hCursor', HANDLE),
('hbrBackground', HANDLE),
('lpszMenuName', LPCSTR),
('lpszClassName', LPCSTR)]
class POINT(Structure):
_fields_ = [('x', c_long),
('y', c_long)]
class MSG(Structure):
_fields_ = [('hwnd', c_int),
('message', c_uint),
('wparam', c_int),
('lparam', c_int),
('time', c_int),
('pt', POINT)]
class RAWINPUTDEVICE(Structure):
_fields_ = [
("usUsagePage", c_ushort),
("usUsage", c_ushort),
("dwFlags", DWORD),
("hwndTarget", HWND),
]
class RAWINPUTHEADER(Structure):
_fields_ = [
("dwType", DWORD),
("dw_size", DWORD),
("hDevice", HANDLE),
("wparam", WPARAM),
]
class RAWMOUSE(Structure):
class _U1(Union):
class _S2(Structure):
_fields_ = [
("usButtonFlags", c_ushort),
("usButtonData", c_ushort),
]
_fields_ = [
("ulButtons", ULONG),
("_s2", _S2),
]
_fields_ = [
("usFlags", c_ushort),
("_u1", _U1),
("ulRawButtons", ULONG),
("lLastX", LONG),
("lLastY", LONG),
("ulExtraInformation", ULONG),
]
_anonymous_ = ("_u1", )
class RAWKEYBOARD(Structure):
_fields_ = [
("MakeCode", c_ushort),
("Flags", c_ushort),
("Reserved", c_ushort),
("VKey", c_ushort),
("Message", UINT),
("ExtraInformation", ULONG),
]
class RAWHID(Structure):
_fields_ = [
("dw_sizeHid", DWORD),
("dwCount", DWORD),
("bRawData", BYTE),
]
class RAWINPUT(Structure):
class _U1(Union):
_fields_ = [
("mouse", RAWMOUSE),
("keyboard", RAWKEYBOARD),
("hid", RAWHID),
]
_fields_ = [
("header", RAWINPUTHEADER),
("_u1", _U1),
("hDevice", HANDLE),
("wparam", WPARAM),
]
_anonymous_ = ("_u1", )
class RawInputReader():
def __init__(self):
pass
def start(self):
ws_overlapped_window = (0 | 12582912 | 524288 | 262144 | 131072 | 65536)
cw_use_default = -2147483648
try:
createwindowex = windll.user32.CreateWindowExA
createwindowex.argtypes = [DWORD, LPCSTR, LPCSTR, DWORD,
c_int, c_int, c_int, c_int, HANDLE,
HANDLE, HANDLE, LPVOID]
createwindowex.restype = HANDLE
wndclass = self.get_window()
# Create Window
hwnd = createwindowex(0,
wndclass.lpszClassName,
b"Python Window",
ws_overlapped_window,
cw_use_default,
cw_use_default,
cw_use_default,
cw_use_default,
0,
0,
wndclass.hInstance,
0)
if (hwnd == 0):
print(WinError())
# Register for raw input
raw_input_device = (2 * RAWINPUTDEVICE)()
self.Rid = raw_input_device
raw_input_device[0].usUsagePage = 0x01
raw_input_device[0].usUsage = 0x06
ridev_input_sink = 0x00000100 # Get events even when not focused
raw_input_device[0].dwFlags = ridev_input_sink
raw_input_device[0].hwndTarget = hwnd
raw_input_device[1].usUsagePage = 0x01
raw_input_device[1].usUsage = 0x02
raw_input_device[1].dwFlags = ridev_input_sink
raw_input_device[1].hwnTarget = hwnd
registerrawinputdevices = windll.user32.RegisterRawInputDevices
registerrawinputdevices(raw_input_device, 2, sizeof(RAWINPUTDEVICE))
self.hwnd = hwnd
except Exception as e:
print("error starting")
print(e)
def get_window(self):
cs_vredraw = 1
cs_hredraw = 2
idi_application = 32512
idc_arrow = 32512
white_brush = 0
# Define Window Class
wndclass = WNDCLASS()
self.wndclass = wndclass
wndclass.style = cs_hredraw | cs_vredraw
wndclass.lpfnWndProc = wndproc(lambda h, m, w, l: self.wndproc(h, m, w, l))
wndclass.cbClsExtra = wndclass.cbWndExtra = 0
wndclass.hInstance = windll.kernel32.GetModuleHandleA(c_int(0))
wndclass.hIcon = windll.user32.LoadIconA(c_int(0), c_int(idi_application))
wndclass.hCursor = windll.user32.LoadCursorA(c_int(0), c_int(idc_arrow))
wndclass.hbrBackground = windll.gdi32.GetStockObject(c_int(white_brush))
wndclass.lpszMenuName = None
wndclass.lpszClassName = b"MainWin"
if not windll.user32.RegisterClassA(byref(wndclass)):
print("error in RegisterClassA")
raise WinError()
return wndclass
def poll_events(self):
# Pump Messages
msg = MSG()
pmsg = pointer(msg)
pm_remove = 1
while windll.user32.PeekMessageA(pmsg, self.hwnd, 0, 0, pm_remove) != 0:
windll.user32.DispatchMessageA(pmsg)
def __del__(self):
pass
def stop(self):
self.Rid[0].dwFlags = 0x00000001
windll.user32.DestroyWindow(self.hwnd)
def wndproc(self, hwnd, message, wparam, lparam):
try:
wm_input = 255
ri_mouse_wheel = 0x0400
wm_destroy = 2
if message == wm_destroy:
windll.user32.PostQuitMessage(0)
return 0
elif message == wm_input:
get_raw_input_data = windll.user32.get_raw_input_data
null = c_int(0)
dw_size = c_uint()
rid_input = 0x10000003
get_raw_input_data(lparam, rid_input, null, byref(dw_size), sizeof(RAWINPUTHEADER))
if dw_size.value == 40:
# Mouse
raw = RAWINPUT()
if get_raw_input_data(lparam,
rid_input,
byref(raw),
byref(dw_size),
sizeof(RAWINPUTHEADER)) == dw_size.value:
rim_typemouse = 0x00000000
if raw.header.dwType == rim_typemouse:
if raw.mouse._u1._s2.usButtonFlags != ri_mouse_wheel:
return 0
return windll.user32.DefWindowProcA(c_int(hwnd), c_int(message), c_int(wparam), c_int(lparam))
except Exception as e:
print("general exception in wndproc")
print(e)
rir = RawInputReader()
rir.start()
time.sleep(5)
rir.stop()
if __name__ == "__main__":
exit(main(*sys.argv[1:]))