diff --git a/GlobalKeyLogger/GlobalKeyLogger.csproj b/GlobalKeyLogger/GlobalKeyLogger.csproj index 9227afc..504760c 100644 --- a/GlobalKeyLogger/GlobalKeyLogger.csproj +++ b/GlobalKeyLogger/GlobalKeyLogger.csproj @@ -1,4 +1,4 @@ -๏ปฟ + WinExe @@ -7,7 +7,8 @@ win-x64 true true - false + false + enable diff --git a/GlobalKeyLogger/Program.cs b/GlobalKeyLogger/Program.cs index 14d6acd..7aaf97a 100644 --- a/GlobalKeyLogger/Program.cs +++ b/GlobalKeyLogger/Program.cs @@ -1,4 +1,4 @@ -๏ปฟusing System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; @@ -8,48 +8,64 @@ class GlobalKeyLogger : ApplicationContext { - private static Stopwatch stopwatch = Stopwatch.StartNew(); - private static StreamWriter writer; + private static readonly Stopwatch stopwatch = Stopwatch.StartNew(); + private static StreamWriter? writer; private static long lastKeyDownTime = 0; private static bool firstKey = true; private static long flightTime = -1; private static IntPtr _hookID = IntPtr.Zero; - private static Dictionary keyDownTimes = new Dictionary(); - private static NotifyIcon trayIcon; + private static readonly Dictionary keyDownTimes = new Dictionary(); + private static NotifyIcon? trayIcon; private static bool isRecording = true; - private static Icon recordIcon; - private static Icon pauseIcon; - private static string logFileName; - private static int keystrokeCount = 0; // ๐Ÿ†• Track number of logged keystrokes + private static Icon? recordIcon; + private static Icon? pauseIcon; + private static string logFileName = string.Empty; + private static int keystrokeCount = 0; + private bool _disposed = false; - private static LowLevelKeyboardProc _proc = HookCallback; + private static readonly LowLevelKeyboardProc _proc = HookCallback; public GlobalKeyLogger() { - // Load icons recordIcon = CreateRedDotIcon(); pauseIcon = CreatePauseIcon(); - // Create tray icon trayIcon = new NotifyIcon() { Icon = recordIcon, Visible = true, - Text = "Global Keylogger (0)" // Initial tooltip + Text = "Global Keylogger (0)" }; - // Create context menu trayIcon.ContextMenuStrip = new ContextMenuStrip(); trayIcon.ContextMenuStrip.Items.Add("Pause", null, PauseLogging); trayIcon.ContextMenuStrip.Items.Add("Exit", null, Exit); trayIcon.DoubleClick += TrayIcon_DoubleClick; - // Setup keylogger CreateNewLogFile(); _hookID = SetHook(_proc); } + protected override void Dispose(bool disposing) + { + if (_disposed) return; + _disposed = true; + if (disposing) + { + if (_hookID != IntPtr.Zero) + { + UnhookWindowsHookEx(_hookID); + _hookID = IntPtr.Zero; + } + writer?.Dispose(); + trayIcon?.Dispose(); + recordIcon?.Dispose(); + pauseIcon?.Dispose(); + } + base.Dispose(disposing); + } + private void CreateNewLogFile() { string timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); @@ -60,81 +76,75 @@ private void CreateNewLogFile() writer.Flush(); } - private void TrayIcon_DoubleClick(object sender, EventArgs e) + private void TrayIcon_DoubleClick(object? sender, EventArgs e) { - MessageBox.Show($"Keylogger is {(isRecording ? "recording" : "paused")}.\nLogged {keystrokeCount} keystrokes.\n\nLogging to:\n{logFileName}\n\nRight-click tray icon for options.", "Global Keylogger"); + MessageBox.Show( + $"Keylogger is {(isRecording ? "recording" : "paused")}.\nLogged {keystrokeCount} keystrokes.\n\nLogging to:\n{logFileName}\n\nRight-click tray icon for options.", + "Global Keylogger"); } - private void PauseLogging(object sender, EventArgs e) + private void PauseLogging(object? sender, EventArgs e) { isRecording = !isRecording; - trayIcon.Icon = isRecording ? recordIcon : pauseIcon; - trayIcon.ContextMenuStrip.Items[0].Text = isRecording ? "Pause" : "Resume"; - UpdateTooltip(); // ๐Ÿ†• Update tooltip on pause/resume + trayIcon!.Icon = isRecording ? recordIcon : pauseIcon; + trayIcon.ContextMenuStrip!.Items[0].Text = isRecording ? "Pause" : "Resume"; + UpdateTooltip(); } - private void Exit(object sender, EventArgs e) + private void Exit(object? sender, EventArgs e) { - trayIcon.Visible = false; - writer?.Close(); - UnhookWindowsHookEx(_hookID); + trayIcon!.Visible = false; + Dispose(); Application.Exit(); } private static IntPtr SetHook(LowLevelKeyboardProc proc) { - using (Process curProcess = Process.GetCurrentProcess()) - using (ProcessModule curModule = curProcess.MainModule!) - { - return SetWindowsHookEx(WH_KEYBOARD_LL, proc, - GetModuleHandle(curModule.ModuleName), 0); - } + using Process curProcess = Process.GetCurrentProcess(); + using ProcessModule curModule = curProcess.MainModule!; + return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0); } private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { - if (nCode >= 0) + if (nCode >= 0 && writer != null && isRecording) { int vkCode = Marshal.ReadInt32(lParam); - if (isRecording) + if (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN) { - if (wParam == (IntPtr)WM_KEYDOWN) - { - long currDownTime = stopwatch.ElapsedMilliseconds; + long currDownTime = stopwatch.ElapsedMilliseconds; - if (firstKey) - { + if (firstKey) + { + flightTime = -1; + firstKey = false; + } + else + { + flightTime = currDownTime - lastKeyDownTime; + if (flightTime > 1500) flightTime = -1; - firstKey = false; - } - else - { - flightTime = currDownTime - lastKeyDownTime; - if (flightTime > 1500) - flightTime = -1; - } - - lastKeyDownTime = currDownTime; - keyDownTimes[vkCode] = currDownTime; } - else if (wParam == (IntPtr)WM_KEYUP) + + lastKeyDownTime = currDownTime; + keyDownTimes[vkCode] = currDownTime; + } + else if (wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP) + { + if (keyDownTimes.TryGetValue(vkCode, out long downTime)) { - if (keyDownTimes.TryGetValue(vkCode, out long downTime)) - { - long releaseTime = stopwatch.ElapsedMilliseconds; - long holdTime = releaseTime - downTime; + long holdTime = stopwatch.ElapsedMilliseconds - downTime; - writer.WriteLine($"{vkCode},{holdTime},{flightTime}"); - writer.Flush(); + writer.WriteLine($"{vkCode},{holdTime},{flightTime}"); + writer.Flush(); - keystrokeCount++; // ๐Ÿ†• Count each logged key - UpdateTooltip(); // ๐Ÿ†• Update tray text + keystrokeCount++; + UpdateTooltip(); - keyDownTimes.Remove(vkCode); - } + keyDownTimes.Remove(vkCode); } } } @@ -145,35 +155,43 @@ private static void UpdateTooltip() { string state = isRecording ? "Recording" : "Paused"; string tooltip = $"Keylogger ({state})\nKeys: {keystrokeCount}"; - trayIcon.Text = tooltip.Length > 63 ? tooltip.Substring(0, 63) : tooltip; + trayIcon!.Text = tooltip.Length > 63 ? tooltip.Substring(0, 63) : tooltip; } private static Icon CreateRedDotIcon() { - Bitmap bmp = new Bitmap(16, 16); + using Bitmap bmp = new Bitmap(16, 16); using (Graphics g = Graphics.FromImage(bmp)) { g.Clear(Color.Transparent); g.FillEllipse(Brushes.Red, 2, 2, 12, 12); } - return Icon.FromHandle(bmp.GetHicon()); + IntPtr hIcon = bmp.GetHicon(); + Icon icon = (Icon)Icon.FromHandle(hIcon).Clone(); + DestroyIcon(hIcon); + return icon; } private static Icon CreatePauseIcon() { - Bitmap bmp = new Bitmap(16, 16); + using Bitmap bmp = new Bitmap(16, 16); using (Graphics g = Graphics.FromImage(bmp)) { g.Clear(Color.Transparent); g.FillRectangle(Brushes.RoyalBlue, 4, 2, 3, 12); g.FillRectangle(Brushes.RoyalBlue, 9, 2, 3, 12); } - return Icon.FromHandle(bmp.GetHicon()); + IntPtr hIcon = bmp.GetHicon(); + Icon icon = (Icon)Icon.FromHandle(hIcon).Clone(); + DestroyIcon(hIcon); + return icon; } private const int WH_KEYBOARD_LL = 13; private const int WM_KEYDOWN = 0x0100; private const int WM_KEYUP = 0x0101; + private const int WM_SYSKEYDOWN = 0x0104; + private const int WM_SYSKEYUP = 0x0105; [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int idHook, @@ -190,6 +208,10 @@ private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr GetModuleHandle(string lpModuleName); + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DestroyIcon(IntPtr hIcon); + [STAThread] static void Main() {