تتبع الامتحان: ExamCookie

لقد علمت أن الحكومة الدنماركية لم تعلق برنامج Digital Exam Monitor ، الذي قمنا بتحليله وتحايلنا عليه تمامًا في المقالة السابقة ، ولكن ربما أغلقت هذا النظام تمامًا بعد أسبوع من إبلاغنا عن طريقة القرصنة. لا أريد أن أظن أن الحكومة الدانمركية رفضت فكرة مراقبة الامتحانات ، لكن تم ملاحظة عملنا بوضوح.

سنشرح في هذه المقالة التفاصيل الفنية لكيفية عمل أداة أخرى لتتبع تلاميذ المدارس: ExamCookie. إذا كنت مهتمًا فقط بتجاوز النظام ، فانتقل إلى القسم المناسب.

ExamCookie


في الآونة الأخيرة ، ضربت هذه الأداة الأخبار بسبب التحقيق في انتهاك إجمالي الناتج المحلي. قررنا إلقاء نظرة على ثاني أكبر منافس لنظام تتبع المدارس المذكور أعلاه خلال الامتحانات: ExamCookie . هذا نظام تتبع تجاري تستخدمه أكثر من 20 مدرسة دنمركية. لا توجد وثائق على الموقع بخلاف الوصف التالي:

ExamCookie هو برنامج بسيط يراقب نشاط الكمبيوتر للطالب أثناء الاختبار للتأكد من اتباع القواعد. يحظر البرنامج على الطلاب استخدام أي شكل غير قانوني من المساعدة.

يحفظ ExamCookie جميع الأنشطة على الكمبيوتر: عناوين URL النشطة ، اتصالات الشبكة ، العمليات ، الحافظة ولقطات الشاشة عند تغيير حجم النافذة.

يعمل البرنامج ببساطة: عن طريق الدخول في الاختبار ، يمكنك تشغيله على جهاز الكمبيوتر الخاص بك ، ويراقب نشاطك. عند الانتهاء من الاختبار ، يتم إغلاق البرنامج ، ويمكنك إزالته من الكمبيوتر.

لبدء التتبع ، تحتاج إلى استخدام تسجيل دخول UNI الخاص بك ، والذي يعمل على مختلف المواقع التعليمية ، أو إدخال بيانات الاعتماد يدويًا. لم نستخدم الأداة ، لذلك لا يمكننا تحديد الحالات التي يتم فيها استخدام الإدخال اليدوي. ربما يتم ذلك للطلاب الذين ليس لديهم تسجيل دخول UNI ، والذي لا نعتبره ممكنًا.



معلومات ثنائية


يمكن تنزيل البرنامج من صفحة ExamCookie الرئيسية. إنه تطبيق x86 .NET. للرجوع إليها ، تحتوي علامة MD5 الثنائية التي تم تحليلها على 63AFD8A8EC26C1DC368D8FF8710E337D ، على EXAMCOOKIE APS بتاريخ 24 أبريل 2019. كما أظهر المقال الأخير ، بالكاد يمكن تحليل تحليل .NET الثنائية باسم الهندسة العكسية ، لأن الجمع بين كود IL والبيانات الوصفية سهلة القراءة يوفر شفرة مصدر مثالية.

بخلاف برنامج المراقبة السابق ، لم يقم مطورو هذه الأداة بحذفه من سجل تصحيح الأخطاء فحسب ، بل قاموا أيضًا بتعطيله. على الأقل حاولوا :-)

التعتيم (الضحك على البكاء)


عندما فتحنا التطبيق في dnSpy ، لاحظنا بسرعة نقطة دخول مفقودة:

 // Token: 0x0600003D RID: 61 RVA: 0x00047BB0 File Offset: 0x00045FB0 [STAThread] [DebuggerHidden] [EditorBrowsable(EditorBrowsableState.Advanced)] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] internal static void Main(string[] Args) { } 

الغريب ، عادة ما يتم افتراض وجود نوع من الغلاف ، فهو يغير أجسام الطرق من مُنشئ الوحدة ، والذي يمتد إلى نقطة الدخول الفعلية ، لنرى:

 // Token: 0x06000001 RID: 1 RVA: 0x00058048 File Offset: 0x00055048 static <Module>() { <Module>.\u206B\u202B\u200B\u206F\u206C\u202D\u200D\u200E\u202D\u206B\u206F\u206F\u202C\u202A\u206B\u202E\u202A\u206C\u202A\u206C\u200B\u206A\u202D\u206C\u202C\u206C\u200F\u202C\u206C\u202C\u200C\u206A\u200C\u206C\u200B\u206B\u202B\u206E\u202C\u202B\u202E(); <Module>.\u206C\u200D\u200F\u200E\u200C\u200C\u200F\u200F\u206E\u206A\u206A\u200B\u202C\u206A\u206B\u200D\u206E\u200E\u202D\u206B\u202C\u206C\u202D\u206D\u200C\u200F\u206E\u200F\u206E\u206A\u202B\u206B\u200E\u206B\u202E\u206F\u206A\u202E\u202C\u202A\u202E(); <Module>.\u200B\u202D\u200F\u200F\u202A\u206D\u202C\u206B\u206E\u202A\u206F\u206C\u200D\u200C\u202D\u200F\u202B\u202C\u202B\u206D\u206D\u202D\u206E\u200D\u206D\u206A\u202A\u202C\u200C\u206F\u206B\u206E\u200D\u202E\u206F\u200C\u206B\u200E\u206D\u206A\u202E(); } 

بارد. إنه عام 2019 ، ولا يزال الناس يستخدمون Confuser (على سبيل المثال).

لقد أدركنا على الفور رمز إلغاء الضغط هذا وتحققنا من رؤوس المجمعات:

  [الوحدة النمطية: ConfusedBy ("Confuser.Core 1.1.0 + a36320377a")] 

في الوقت الحالي ، كنا نظن أن الشفرة سوف يتم حجبها فعليًا ، نظرًا لأن المنشئ المذكور أعلاه يقوم بفك تشفير أجسام وموارد هذه الطريقة. ولكن لمفاجأتنا ، قرر مطور التشويش ... عدم إعادة تسمية البيانات الوصفية:



هذا يقتل كل طنين الهندسة العكسية. كما قلنا في مقال سابق ، أود أن أواجه المشكلة الحقيقية المتمثلة في أداة مراقبة محمية بشكل جيد وعالية الجودة ، سيستغرق تحليلها أكثر من خمس دقائق.

في أي حال ، يكون تفريغ أي ثنائي محمي بواسطة confuser (على سبيل المثال) بسيطًا للغاية: استخدم dumper .NET binaries أو نقطة توقف بيان الفاصل في <MODULE> .ctor وتفريغ نفسك. تستغرق العملية 30 ثانية ، وسيظل هذا الرابط دائمًا هو المفضل لدي ، لأن الحماية من تصحيح الأخطاء لا تعمل أبدًا على الإطلاق .

قررنا استخدام MegaDumper: هذا أسرع قليلاً من الإغراق يدويًا:



بعد الإغراق في ملف ExamCookie الثنائي ، يجب أن تظهر الرسالة التالية:



لديك الآن دليل يحتوي على جميع أجزاء المجمّع التي تم تحميلها في العملية المقابلة ، وهذه المرة مع نصوص طريقة فك التشفير.

كل من نفذ هذا التشويش والحمد لله على الأقل قام بتشفير الخطوط:

 else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.SymbolicLink)) { Module1.DebugPrint(<Module>.smethod_5<string>(1582642794u), new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.Tiff)) { Module1.DebugPrint(<Module>.smethod_2<string>(4207351461u), new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.UnicodeText)) { Module1.DebugPrint(<Module>.smethod_5<string>(3536903244u), new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.WaveAudio)) { Module1.DebugPrint(<Module>.smethod_2<string>(2091555364u), new object[0]); } 

نعم ، تشفير السلسلة القديمة الجيد Confuser (Ex) ، أفضل أمان زائف في عالم .NET. من الجيد أن يتم اختراق Confuser (Ex) في كثير من الأحيان بحيث تتوفر أدوات إلغاء تجزئة على الإنترنت لكل آلية ، لذلك لن نلمس أي شيء متعلق بـ .NET. تشغيل ConfuserExStringDecryptor من CodeCracker على تفريغ ثنائي:



يحول المقتطف السابق إلى هذا:

 else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.SymbolicLink)) { Module1.DebugPrint("ContainsData.SymbolicLink", new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.Tiff)) { Module1.DebugPrint("ContainsData.Tiff", new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.UnicodeText)) { Module1.DebugPrint("ContainsData.UnicodeText", new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.WaveAudio)) { Module1.DebugPrint("ContainsData.WaveAudio", new object[0]); } 

هذا كل ما يتعلق بحماية التطبيق ، المقطوعة في أقل من دقيقة ... لن ننشر أدواتنا هنا ، لأننا لم نطورها وليس لدينا شفرة المصدر. ولكن أي شخص يريد تكرار الوظيفة يمكنه العثور عليها على Tuts4You . لم يعد لدينا حساب tuts4you ، لذلك لا يمكننا الارتباط بالمرايا.

وظائف


والمثير للدهشة أنه لم يتم العثور على "وظيفة مخفية" حقيقية. كما هو موضح في موقع الويب ، يتم إرسال المعلومات التالية بشكل دوري إلى الخادم:

  • قائمة العمليات (كل 5000 مللي ثانية)
  • تطبيق نشط (كل 1000 مللي ثانية)
  • الحافظة (كل 500 مللي ثانية)
  • لقطة شاشة (كل 5000 مللي ثانية)
  • قائمة محولات الشبكة (كل 20000 مللي ثانية)

ما تبقى من التطبيق ممل جدًا ، لذلك قررنا تخطي إجراء التهيئة بالكامل والانتقال مباشرةً إلى الوظائف المسؤولة عن التقاط المعلومات.

محول


يتم تجميع محولات الشبكة بواسطة وظيفة NET NetworkInterface.GetAllNetworkInterfaces() ، تمامًا كما في المقالة السابقة :

 NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface networkInterface in allNetworkInterfaces) { try { // ... // TEXT FORMATTING OMITTED // ... dictionary.Add(networkInterface.Id, stringBuilder.ToString()); stringBuilder.Clear(); } catch (Exception ex) { AdapterThread.OnExceptionEventHandler onExceptionEvent = this.OnExceptionEvent; if (onExceptionEvent != null) { onExceptionEvent(ex); } } } result = dictionary; 

تطبيق نشط


هذا هو الحصول على اهتمام. بدلاً من تسجيل جميع النوافذ المفتوحة ، تتحكم الأداة في التطبيق النشط فقط. التنفيذ مبالغ فيه ، وبالتالي نقدم رمزًا زائفًا جميلًا:

 var whiteList = { "devenv", "ExamCookie.WinClient", "ExamCookie.WinClient.vshost", "wermgr", "ShellExperienceHost" }; // GET WINDOW INFORMATION var foregroundWindow = ApplicationThread.GetForegroundWindow(); ApplicationThread.GetWindowRect(foregroundWindow, ref rect); ApplicationThread.GetWindowThreadProcessId(foregroundWindow, ref processId); var process = Process.GetProcessById(processId); if (process == null) return; // LOG BROWSER URL if (IsBrowser(process)) { var browserUrl = UiAutomation32.GetBrowserUrl(process.Id, process.ProcessName); // SEND BROWSER URL TO SERVER if (ValidBrowserUrl(browserUrl)) { ReportToServer(browserUrl); } } else if (!whiteList.contains(process.ProcessName, StringComparer.OrdinalIgnoreCase)) { ReportToServer(process.MainWindowTitle); } 

رائع ... لا يزال الناس يستخدمون أسماء العمليات للتمييز بينهم. لا يتوقفون ولا يفكرون أبدًا: "انتظر لحظة ، يمكنك تغيير أسماء العمليات كما تريد" ، حتى نتمكن من تجاوز هذه الحماية بأمان.

إذا قرأت مقالًا سابقًا عن برنامج آخر لتتبع الامتحانات ، فربما تتعرف على هذا التطبيق الفرعي للمتصفحات:

 private bool IsBrowser(System.Diagnostics.Process proc) { bool result; try { string left = proc.ProcessName.ToLower(); if (Operators.CompareString(left, "iexplore", false) != 0 && Operators.CompareString(left, "chrome", false) != 0 && Operators.CompareString(left, "firefox", false) != 0 && Operators.CompareString(left, "opera", false) != 0 && Operators.CompareString(left, "cliqz", false) != 0) { if (Operators.CompareString(left, "applicationframehost", false) != 0) { result = false; } else { result = proc.MainWindowTitle.Containing("Microsoft Edge"); } } else { result = true; } } catch (Exception ex) { result = false; } return result; } 

 private string GetBrowserName(string name) { if (Operators.CompareString(name.ToLower(), "iexplore", false) == 0) { return "IE-Explorer"; } else if (Operators.CompareString(name.ToLower(), "chrome", false) == 0) { return "Chrome"; } else if (Operators.CompareString(name.ToLower(), "firefox", false) == 0) { return "Firefox"; } else if (Operators.CompareString(name.ToLower(), "opera", false) == 0) { return "Opera"; } else if (Operators.CompareString(name.ToLower(), "cliqz", false) == 0) { return "Cliqz"; } else if (Operators.CompareString(name.ToLower(), "applicationframehost", false) == 0) { return "Microsoft Edge"; } return ""; } 

والكرز على الكعكة:

 private static string GetBrowserUrlById(object processId, string name) { // ... automationElement.GetCurrentPropertyValue(/*...*/); return url; } 

هذا هو حرفيا نفس التنفيذ كما في المادة السابقة. من الصعب أن نفهم كيف أن المطورين ما زالوا لم يدركوا مدى سوء الأمر. يمكن لأي شخص تحرير عنوان URL في المتصفح ، وهذا لا يستحق حتى التوضيح.

اكتشاف الجهاز الظاهري


على عكس ما يقوله الموقع ، فإن البدء في جهاز افتراضي يضع علامة. التنفيذ ... مثير للاهتمام.

 File.WriteAllBytes("ecvmd.exe", Resources.VmDetect); using (Process process = new Process()) { process.StartInfo = new ProcessStartInfo("ecvmd.exe", "-d") { CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true }; process.Start(); try { using (StreamReader standardOutput = process.StandardOutput) { result = standardOutput.ReadToEnd().Replace("\r\n", ""); } } catch (Exception ex3) { result = "-5"; } } 

حسنًا ، لسبب ما ، يكتبون ثنائيًا خارجيًا على القرص ويقومون بتنفيذه ، ثم يعتمدون تمامًا على نتائج I / O. يحدث هذا في كثير من الأحيان ، لكن نقل مثل هذا العمل المهم إلى عملية أخرى غير محمية هو الأمر كذلك. لنرى أي ملف نتعامل معه:



حتى الآن نستخدم C ++؟ حسنًا ، التشغيل البيني ليس سيئًا بالضرورة. وهذا قد يعني أن لدينا الآن حقا للعمل على الهندسة العكسية (!). دعنا ننظر إلى المؤسسة الدولية للتنمية:

 int __cdecl main(int argc, const char **argv, const char **envp) { int v3; // ecx BOOL v4; // ebx int v5; // ebx int *v6; // eax int detect; // eax bool vbox_key_exists; // bl char vpcext; // bh char vmware_port; // al char *vmware_port_exists; // ecx char *vbox_detected; // edi char *vpcext_exists; // esi int v14; // eax int v15; // eax int v16; // eax int v17; // eax int v18; // eax int v20; // [esp+0h] [ebp-18h] HKEY result; // [esp+Ch] [ebp-Ch] HKEY phkResult; // [esp+10h] [ebp-8h] if ( argc != 2 ) goto LABEL_20; v3 = strcmp(argv[1], "-d"); if ( v3 ) v3 = -(v3 < 0) | 1; if ( !v3 ) { v4 = (unsigned __int8)vm_detect::vmware_port() != 0; result = 0; v5 = (vm_detect::vpcext() != 0 ? 2 : 0) + v4; RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\DSDT\\VBOX__", 0, 0x20019u, &result); v6 = sub_402340(); LABEL_16: sub_404BC0((int)v6, v20); return 0; } detect = strcmp(argv[1], "-s"); if ( detect ) detect = -(detect < 0) | 1; if ( !detect ) { LABEL_20: phkResult = 0; vbox_key_exists = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\ACPI\\DSDT\\VBOX__", 0, 0x20019u, &phkResult) == 0; vpcext = vm_detect::vpcext(); vmware_port = vm_detect::vmware_port(); vmware_port_exists = "1"; vbox_detected = "1"; if ( !vbox_key_exists ) vbox_detected = "0"; vpcext_exists = "1"; if ( !vpcext ) vpcext_exists = "0"; if ( !vmware_port ) vmware_port_exists = "0"; result = (HKEY)vmware_port_exists; v14 = std::print((int)&dword_433310, "VMW="); v15 = std::print(v14, (const char *)result); v16 = std::print(v15, ",VPC="); v17 = std::print(v16, vpcext_exists); v18 = std::print(v17, ",VIB="); v6 = (int *)std::print(v18, vbox_detected); goto LABEL_16; } return 0; } 

هذا يتحقق من وجود منفذ VMWare I / O 'VX':

 int __fastcall vm_detect::vmware_port() { int result; // eax result = __indword('VX'); LOBYTE(result) = 0; return result; } 

بعد ذلك ، يتم التحقق من تنفيذ تعليمات تمديد الكمبيوتر الظاهري ، والتي يجب أن تعمل فقط عند تشغيلها في بيئة افتراضية ، إذا لم يؤدي ذلك إلى تعطل الجهاز إذا تمت معالجته بشكل غير صحيح ؛):

 char vm_detect::vpcext() { char result; // al result = 1; __asm { vpcext 7, 0Bh } return result; } 

... لا الهندسة العكسية الحقيقية ، سوى 30 ثانية لإعادة تسمية وظيفتين :(

هذا البرنامج ببساطة يقرأ مفتاح التسجيل ويقوم بتشغيل اثنين من برامج مراقبة برنامج Hypervisor التي تبدو غريبة مقارنة بالبرنامج الآخر. وأتساءل أين نسختها؟ أوه ، انظر ، مقالة بعنوان "طرق لاكتشاف الأجهزة (كذا الظاهري)" التي تشرح هذه الطرق :). في أي حال ، يمكن التحايل على متجهات الكشف هذه عن طريق تحرير ملف .vmx أو باستخدام إصدار محسن من أي برنامج مراقبة من اختيارك.

حماية البيانات


كما ذكرنا سابقًا ، يجري التحقيق بشأن عدم الامتثال للناتج المحلي الإجمالي ، ويذكر موقع الويب الخاص به :

يتم تشفير البيانات وإرسالها إلى خادم Microsoft Azure آمن ، والذي لا يمكن الوصول إليه إلا باستخدام بيانات الاعتماد الصحيحة. بعد الامتحان ، يتم تخزين البيانات لمدة تصل إلى ثلاثة أشهر.

لسنا متأكدين تمامًا من الكيفية التي يحددون بها "أمان" الخادم ، نظرًا لأن بيانات الاعتماد مضمنة في التطبيق ويتم تخزينها بنص واضح تمامًا في موارد بيانات التعريف:

  نقطة النهاية: https://examcookiewinapidk.azurewebsites.net
 اسم المستخدم: VfUtTaNUEQ
 كلمة المرور: AwW9PHjVc 

لم نفحص محتويات الخادم (هذا غير قانوني) ، لكن يمكننا افتراض أن الوصول الكامل متوفر هناك. نظرًا لأن الحساب مضمن في التطبيق ، فلا يوجد عزل بين حاويات بيانات الطلاب.

إخلاء المسؤولية القانونية: نحتفظ بالحق في نشر بيانات اعتماد واجهة برمجة التطبيقات لأنها مخزنة في ملف ثنائي عام وبالتالي لا يتم الحصول عليها بطريقة غير قانونية. ومع ذلك ، فإن استخدامها بقصد خبيث ينتهك بوضوح القانون ، لذلك ، نوصي بشدة ألا يستخدم القراء بيانات الاعتماد المذكورة أعلاه بأي شكل من الأشكال ، وأنهم غير مسؤولين عن أي إجراءات محتملة.

التفاف


نظرًا لأن هذا التطبيق يشبه بشكل لا يصدق مراقب الإمتحانات الرقمية ، فقد قمنا بتحديث كود ayyxam لدعم ExamCookie.

قائمة العمليات


ntdll!NtQuerySystemInformation واجهة معالجة .NET داخليًا بيانات العملية باستخدام استدعاء النظام ntdll!NtQuerySystemInformation . لإخفاء العمليات من ذلك يتطلب بعض العمل ، لأنه يتم الإشارة إلى معلومات حول العملية في العديد من الأماكن. لحسن الحظ ، يسترجع .NET نوعًا واحدًا فقط من المعلومات ، لذلك ليس عليك استخدام جميع أساليب المتأخرات .

رمز لتجاوز التحقق من صحة العمليات النشطة.

 NTSTATUS WINAPI ayyxam::hooks::nt_query_system_information( SYSTEM_INFORMATION_CLASS system_information_class, PVOID system_information, ULONG system_information_length, PULONG return_length) { // DONT HANDLE OTHER CLASSES if (system_information_class != SystemProcessInformation) return ayyxam::hooks::original_nt_query_system_information( system_information_class, system_information, system_information_length, return_length); // HIDE PROCESSES const auto value = ayyxam::hooks::original_nt_query_system_information( system_information_class, system_information, system_information_length, return_length); // DONT HANDLE UNSUCCESSFUL CALLS if (!NT_SUCCESS(value)) return value; // DEFINE STRUCTURE FOR LIST struct SYSTEM_PROCESS_INFO { ULONG NextEntryOffset; ULONG NumberOfThreads; LARGE_INTEGER Reserved[3]; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ImageName; ULONG BasePriority; HANDLE ProcessId; HANDLE InheritedFromProcessId; }; // HELPER FUNCTION: GET NEXT ENTRY IN LINKED LIST auto get_next_entry = [](SYSTEM_PROCESS_INFO* entry) { return reinterpret_cast<SYSTEM_PROCESS_INFO*>( reinterpret_cast<std::uintptr_t>(entry) + entry->NextEntryOffset); }; // ITERATE AND HIDE PROCESS auto entry = reinterpret_cast<SYSTEM_PROCESS_INFO*>(system_information); SYSTEM_PROCESS_INFO* previous_entry = nullptr; for (; entry->NextEntryOffset > 0x00; entry = get_next_entry(entry)) { constexpr auto protected_id = 7488; if (entry->ProcessId == reinterpret_cast<HANDLE>(protected_id) && previous_entry != nullptr) { // SKIP ENTRY previous_entry->NextEntryOffset += entry->NextEntryOffset; } // SAVE PREVIOUS ENTRY FOR SKIPPING previous_entry = entry; } return value; } 

عازلة


ole32.dll!OleGetClipboard ، والتي هي عرضة للغاية لخطافات ، هي المسؤولة عن التنفيذ الداخلي للمخازن المؤقتة في .NET. بدلاً من قضاء الكثير من الوقت في تحليل الهياكل الداخلية ، يمكنك ببساطة إرجاع S_OK ، وستقوم معالجة أخطاء .NET بالباقي:

 std::int32_t __stdcall ayyxam::hooks::get_clipboard(void* data_object[[maybe_unused]]) { // LOL return S_OK; } 

سيؤدي هذا إلى إخفاء المخزن المؤقت بأكمله من أداة المراقبة ExamCookie دون الإخلال بوظائف البرنامج.

لقطات


كما هو الحال دائمًا ، يأخذ الأشخاص تطبيق .NET جاهزًا للوظيفة المطلوبة. للتغلب على هذه الوظيفة ، لم يكن علينا حتى تغيير أي شيء في الكود السابق. يتم التحكم في لقطات الشاشة بواسطة وظيفة Graphics.CopyFromScreen .NET. إنها في الأساس غلاف لنقل كتل البت ، والتي تستدعي gdi32!BitBlt كما هو الحال في ألعاب الفيديو لمكافحة أنظمة مكافحة الغش التي تلتقط لقطات ، يمكننا استخدام ربط BitBlt وإخفاء أي معلومات غير مرغوب فيها قبل التقاط لقطة شاشة.


فتح المواقع


يتم نسخ عنوان URL الخاص بالممسك بالكامل من البرنامج السابق ، لذا يمكننا مرة أخرى إعادة استخدام الكود الخاص بنا لتجاوز الحماية. في المقالة الأخيرة ، قمنا بتوثيق بنية AutomationElement ، مما أدى إلى إطلاق الخطاف التالي:

 std::int32_t __stdcall ayyxam::hooks::get_property_value(void* handle, std::int32_t property_id, void* value) { constexpr auto value_value_id = 0x755D; if (property_id != value_value_id) return ayyxam::hooks::original_get_property_value(handle, property_id, value); auto result = ayyxam::hooks::original_get_property_value(handle, property_id, value); if (result != S_OK) // SUCCESS? return result; // VALUE URL IS STORED AT 0x08 FROM VALUE STRUCTURE class value_structure { public: char pad_0000[8]; //0x0000 wchar_t* value; //0x0008 }; auto value_object = reinterpret_cast<value_structure*>(value); // ZERO OUT OLD URL std::memset(value_object->value, 0x00, std::wcslen(value_object->value) * 2); // CHANGE TO GOOGLE.COM constexpr wchar_t spoofed_url[] = L"https://google.com"; std::memcpy(value_object->value, spoofed_url, sizeof(spoofed_url)); return result; } 

اكتشاف الجهاز الظاهري


يمكن التحايل على الاكتشاف البطيء لجهاز ظاهري بطريقتين: 1) تصحيح برنامج تم مسحه على القرص ؛ أو 2) إعادة توجيه عملية إنشاء العملية إلى تطبيق وهمية. هذا الأخير يبدو أكثر بساطة :). لذا ، داخليًا ، تستدعي Process.Start() CreateProcess ، لذا قم فقط بتوصيله وإعادة توجيهه إلى أي تطبيق وهمية يطبع الحرف '0'.

 BOOL WINAPI ayyxam::hooks::create_process( LPCWSTR application_name, LPWSTR command_line, LPSECURITY_ATTRIBUTES process_attributes, LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD creation_flags, LPVOID environment, LPCWSTR current_directory, LPSTARTUPINFOW startup_information, LPPROCESS_INFORMATION process_information ) { // REDIRECT PATH OF VMDETECT TO DUMMY APPLICATION constexpr auto vm_detect = L"ecvmd.exe"; if (std::wcsstr(application_name, vm_detect)) { application_name = L"dummy.exe"; } return ayyxam::hooks::original_create_process( application_name, command_line, process_attributes, thread_attributes, inherit_handles, creation_flags, environment, current_directory, startup_information, process_information); } 

تحميل


المشروع بأكمله متاح في مستودع جيثب . يعمل البرنامج عن طريق حقن x86 الثنائي في العملية المقابلة.

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


All Articles