At CYFIRMA, we are dedicated to providing timely intelligence on emerging cyber threats and adversarial tactics that target both individuals and organizations. This report delivers a comprehensive analysis of the TinkyWinkey Keylogger, highlighting its use of advanced Windows system programming techniques, such as service management, low-level keyboard hooks, process handling, and system information collection. TinkyWinkey operates stealthily as both a service and an executable, capturing extensive system data including CPU specifications, memory, OS version, and network details, while continuously monitoring active windows and keyboard layouts. The malware employs low-level hooks to record all keystrokes, accurately handling special keys, media keys, and Unicode characters across multiple languages. Its loader performs reliable DLL injection into trusted processes, and a dedicated service thread ensures persistent execution of the payload within the user session. First observed June 24–25, TinkyWinkey exemplifies sophisticated keylogging and system monitoring capabilities, making it a high-value target for threat intelligence and mitigation efforts.
TinkyWinkey is a Windows-based project demonstrating a Windows service and keylogger system. It consists of two main components:
Technologies & APIs: C/C++, MSVC, NMAKE, Windows x86/x64, using SCM API, Windows Hooks API, Terminal Services API, Winsock2, and File API.
System Information Gathering: Collects CPU details (vendor, model, architecture, core count), Windows version and build, RAM size, and local IP address.
Keylogging: Implements low-level keyboard hooks to capture all input, including alphanumeric, function, navigation, modifier, special, and media keys; supports Unicode and detects keyboard layout changes.
Window Tracking: Monitors foreground window changes and logs window titles with timestamps.
Logging System: Writes all data to UTF-8 encoded log files with timestamp integration.
Service Management: Provides full control over the Tinky service lifecycle.
Process & Session Management: Launches winkey.exe in the user session for stealthy execution.
The malware retrieves the system’s temporary directory by calling GetTempPathW() and appending the filename logs_tw.txt by calling CreateFileW() with OPEN_ALWAYS, ensuring the log file is created if it does not exist. Using SetFilePointer(), the file handle is moved to the end, enabling append mode logging. Captured keystrokes are written into this file through WriteFile(), with the function write_to_file() serving as the logging routine.
It then subsequently invokes multiple information-gathering functions, such as get_windows_info(), get_cpu_info(), get_ip_address(), and get_ram_info(), which are responsible for collecting host system details, including OS version, processor information, network identifiers, and memory capacity.
The function get_windows_info() leverages the undocumented RtlGetVersion API from ntdll.dll to query the operating system’s version information. Instead of using standard Windows APIs like GetVersionEx() (which can be inaccurate due to application compatibility shims), the malware retrieves a direct pointer to RtlGetVersion via GetProcAddress(). It then populates the RTL_OSVERSIONINFOW structure with details, such as major version, minor version, and build number.
The get_cpu_info() function collects detailed processor information through a combination of low-level instructions and Windows API calls. It first invokes the __cpuid intrinsic, extracting the CPU vendor string and extended brand string to identify the exact processor model. The function concatenates results from multiple CPUID calls (0x80000002–0x80000004) to reconstruct the full CPU brand name. It then calls GetSystemInfo() to retrieve system-level details such as the processor architecture (x86, x64, ARM, etc.) and the number of logical processors.
The get_ip_address() function enables the malware to enumerate the victim machine’s network identity. It initializes Winsock (WSAStartup) and retrieves the local system’s hostname using gethostname(). The hostname is then resolved into an IPv4 address via getaddrinfo().
The get_ram_info() function collects details about the victim machine’s physical memory. It leverages the Windows API GlobalMemoryStatusEx() to query total system resources, specifically the size of installed physical RAM. The value returned (ullTotalPhys) is converted into megabytes and written to the malware’s log file via write_to_file()
A WinEventHook on EVENT_SYSTEM_FOREGROUND is set to detect whenever the user switches active applications. This lets the attacker map keystrokes to specific applications (e.g., browser, email client, banking portal), enhancing the value of stolen credentials.
The malware installs a low-level keyboard hook (WH_KEYBOARD_LL) that captures all keystrokes across the system.
The malware monitors the active keyboard layout (HKL). If the user switches languages (e.g., English → Russian), the malware logs the new layout. This helps attackers understand what language or character set the victim is typing in, making the stolen keystrokes more meaningful.
It intercepts keyboard events from the low-level hook. For each key press (WM_KEYDOWN), it extracts the virtual key code (vkCode) and converts it into a readable format. Special keys (Escape, Enter, Space, Backspace, etc.) are replaced with descriptive markers like [ESC] or [ENTER] in the log.
For non-special keys, the malware retrieves the current keyboard state (GetKeyboardState) and converts the virtual key code and scan code into a Unicode character using ToUnicode(). It then converts the resulting character into UTF-8 (WideCharToMultiByte) and writes it to the log file via its logging routine. This ensures that normal alphanumeric and symbol input is captured accurately, reflecting modifiers like Shift or Caps Lock, and supports multi-language environments.
Finally, it enters a continuous message loop (GetMessage, TranslateMessage, DispatchMessage) to keep its foreground window and keyboard hooks active. This ensures persistent monitoring of user activity until termination. Upon exit, it unregisters the hooks (UnhookWinEvent, UnhookWindowsHookEx) to clean up resources, preventing system instability or leaving traces in memory. This design maintains stealthy and continuous keylogging operations.
Keylogger.dll is the primary payload responsible for low-level keystroke capture, foreground window correlation, keyboard layout tracking, and structured log emission. It is designed to be injected into a trusted process. The DLL initializes global hooks, registers window-focus notifications, and persists a message loop to continuously process input events.
The loader creates a log file in the temporary folder to record errors and status messages. It also verifies that the malicious DLL (keylogger.dll) exists in the same directory as the loader before proceeding, ensuring the payload is available for injection.
The loader first identifies the PID of the target process (explorer.exe) using FindTargetPID. Once identified, the malware obtains a handle to the process with full access rights, enabling memory allocation, thread creation, and DLL injection. If the process is not found, it logs an error and exits.
The loader allocates a memory block inside the target process using VirtualAllocEx, sized to store the full DLL path. This memory will hold the path of the DLL to be loaded remotely. Failure to allocate memory halts the injection, preventing instability or crashes in the target process.
The loader writes the DLL path into the allocated memory and creates a remote thread in the target process using LoadLibraryW. This forces the target process to load and execute the DLL in its own context. Errors in memory writing or thread creation are logged, and the process is safely cleaned up.
After the remote thread is launched, the loader waits for it to complete using WaitForSingleObject. Logging a success message provides confirmation that the DLL injection was completed without errors. This technique ensures stealthy and reliable DLL execution within a trusted process.
The Service worker thread (ServiceWorkerThread()) is responsible for launching the malware payload (winkey.exe) in the active user session. It first retrieves the service executable’s path and extracts its directory to construct the full path to the payload. It then prepares the STARTUPINFO structure, specifying the winsta0\default desktop and hiding the window (SW_HIDE) to ensure stealthy execution. The PROCESS_INFORMATION structure is initialized to receive process handles, and CreateProcessAsUser is called with the duplicated user token (htoken) to run winkey.exe in the user’s context. This allows the malware to execute invisibly with user privileges, while maintaining control over the process.
The WinMain function acts as the primary execution entry point for TinkyWinkey. It manages service lifecycle operations-installation, start, stop, and deletion-via command-line arguments, while defaulting to automatic execution under the Service Control Manager (SCM). This dual-mode design ensures persistent execution of the keylogger across system reboots, enabling stealthy monitoring of user activity and system information.
Upon execution, svc.exe registers a malicious Windows service named “Tinky”. The service is configured with an automatic startup type, enabling the malware to achieve persistence by ensuring that the service is invoked every time the system boots. This guarantees that the keylogger remains active without requiring user interaction.
After establishing persistence, svc.exe proceeds to execute the secondary payload winkey.exe, which is the primary keylogging module, responsible for monitoring user activity in real time. It continuously intercepts keystrokes, tracks active foreground window titles, and captures additional contextual data that could enhance the value of the stolen information.
During its operation, the malware creates a log file in the user’s temp directory named Logs_tw.txt. This file acts as a central repository where both system reconnaissance details (such as OS version, hostname, and hardware information) and user activity data (keystrokes and window focus changes) are recorded. The structured nature of the log file allows the attacker to easily parse and exfiltrate sensitive data, including credentials, chat communications, and other confidential input.
On June 24–25, the TinkyWinkey – Yet Another Windows 10 Keylogger was identified, and believed to be published by a user, who claims to be based in Lyon, France.
TinkyWinkey represents a highly capable and stealthy Windows-based keylogger that combines persistent service execution, low-level keyboard hooks, and comprehensive system profiling to gather sensitive information. Its ability to capture all keystrokes, including special keys and multi-language input, alongside detailed system metrics such as CPU, memory, OS version, and network identifiers, underscores the sophistication of modern malware targeting endpoint systems. The loader’s use of DLL injection into trusted processes and service-based persistence highlights the challenge of detection and mitigation. First observed in late June 2025, this malware exemplifies the evolving threat landscape, where attackers leverage advanced programming techniques to maintain stealth and maximize data capture. Organizations should remain vigilant by monitoring unusual service activity, unexpected DLL injections, and persistent logging patterns. Proactive threat intelligence, continuous endpoint monitoring, and rapid response mechanisms are critical to minimizing exposure and safeguarding sensitive user and system data against such sophisticated threats.
Indicator | Type | Remarks |
fe6a696e7012696f2e94a4d31b2f076f32c71d44e4c3cec69a6984ef0b81838a | Sha256 | svc.exe |
7834a64c39f85db5f073d76ddb453c5e23ad18244722d6853986934b750259fd | Sha256 | winkey.exe |
eb6752e60170199e4ce4d5de72fb539f807332771e1a668865aac1eee2c01d93 | Sha256 | keylogger.dll |
Tactic | Technique ID | Technique |
Execution | T1059 | Command and Scripting Interpreter |
T1129 | Shared Modules | |
T1569.002 | Service Execution | |
T1218.011 | System Binary Proxy Execution: Rundll32 | |
Persistence | T1543.003 | Create or Modify System Process: Windows Service |
Privilege Escalation | T1055.003 | Process Injection: Thread Execution Hijacking |
Defense Evasion | T1620 | Reflective Code Loading |
T1218.011 | System Binary Proxy Execution: Rundll32 | |
T1497 | Virtualization/Sandbox Evasion | |
Discovery | T1057 | Process Discovery |
T1082 | System Information Discovery | |
T1083 | File and Directory Discovery | |
T1518 | Software Discovery | |
T1518.001 | Security Software Discovery | |
T1614.001 | System Location Discovery: System Language | |
Credential Access | T1056.001 | Input Capture: Keylogging |
Collection | T1056.001 | Input Capture: Keylogging |
Impact | T1489 | Service Stop |
import “hash”
rule TinkyWinkey_Keylogger_BySHA256
{
meta:
author = “CYFIRMA”
date = “2025-08-26”
description = “Detects TinkyWinkey components by SHA-256 (svc.exe, winkey.exe, keylogger.dll)”
sha256_1 = “fe6a696e7012696f2e94a4d31b2f076f32c71d44e4c3cec69a6984ef0b81838a” // svc.exe
sha256_2 = “7834a64c39f85db5f073d76ddb453c5e23ad18244722d6853986934b750259fd” // winkey.exe
sha256_3 = “eb6752e60170199e4ce4d5de72fb539f807332771e1a668865aac1eee2c01d93” // keylogger.dll
reference = “GitHub: TinkyWinkey keylogger”
condition:
uint16(0) == 0x5A4D and // PE ‘MZ’ header
filesize < 100MB and
(
hash.sha256(0, filesize) == “fe6a696e7012696f2e94a4d31b2f076f32c71d44e4c3cec69a6984ef0b81838a” or
hash.sha256(0, filesize) == “7834a64c39f85db5f073d76ddb453c5e23ad18244722d6853986934b750259fd” or
hash.sha256(0, filesize) == “eb6752e60170199e4ce4d5de72fb539f807332771e1a668865aac1eee2c01d93”
)
}
Strategic Recommendations:
Tactical Recommendations:
Operational Recommendations:
Technical Recommendations: