نواصل اليوم قصة كيف نقوم ، مع رفاق جامعة Innopolis ، بتطوير تقنية Active Restore للسماح للمستخدم بالبدء في العمل على أجهزته في أقرب وقت ممكن بعد الفشل. سنتحدث عن تطبيقات Windows الأصلية ، بما في ذلك ميزات إنشائها وإطلاقها. تحت القص - قليلا عن مشروعنا ، فضلا عن دليل عملي حول كيفية كتابة التطبيقات الأصلية.

في المنشورات السابقة ، تحدثنا بالفعل عن ماهية
Active Restore وكيف يطور الطلاب من Innopolis
الخدمة . اليوم أريد أن أركز على التطبيقات الأصلية ، التي نريد "دفن" خدمة الاسترداد النشطة الخاصة بها. إذا نجح كل شيء ، فيمكننا:
- قبل ذلك بكثير لبدء الخدمة نفسها
- قبل ذلك بكثير للاتصال بالسحابة التي يكمن النسخ الاحتياطي
- من السابق لأوانه فهم الوضع في النظام - التمهيد العادي أو الاسترداد
- لاستعادة الملفات أقل بكثير مقدما
- اسمح للمستخدم بالبدء بشكل أسرع.
ما هو التطبيق الأصلي بشكل عام؟
للإجابة على هذا السؤال ، دعونا نلقي نظرة على سلسلة المكالمات التي يجريها النظام ، على سبيل المثال ، إذا حاول مبرمج في تطبيقه إنشاء ملف.
بافل يوسيفوفيتش - برمجة Windows Kernel (2019)يستخدم المبرمج الدالة
CreateFile ، والتي تم التصريح عنها في ملف رأس fileapi.h ويتم تنفيذها في Kernel32.dll. ومع ذلك ، لا تنشئ هذه الوظيفة نفسها ملفًا ؛ فهي تتحقق فقط من الوسائط عند الإدخال
وتستدعي الدالة
NtCreateFile (تشير بادئة Nt فقط إلى أن الوظيفة أصلية). يتم الإعلان عن هذه الوظيفة في ملف الرأس winternl.h ويتم تنفيذها في ntdll.dll. إنها تستعد للقفز إلى الفضاء النووي ، وبعد ذلك تقوم بإجراء مكالمة نظام لإنشاء ملف. في هذه الحالة ، اتضح أن Kernel32 مجرد غلاف لـ Ntdll. أحد الأسباب وراء ذلك ، لدى Microsoft القدرة على تغيير وظائف العالم الأصلي ، ولكن لا تلمس الواجهات القياسية. لا توصي Microsoft باستدعاء الوظائف الأصلية مباشرةً ولا توثق معظمها. بالمناسبة ، يمكن العثور على ميزات غير موثقة
هنا .
الميزة الرئيسية للتطبيقات الأصلية هي أن ntdll يتم تحميله في النظام في وقت أبكر بكثير من kernel32. هذا منطقي ، لأن kernel32 يتطلب Ntdll للعمل. نتيجة لذلك ، يمكن أن تبدأ التطبيقات التي تستخدم الوظائف الأصلية في العمل قبل ذلك بكثير.
وبالتالي ، فإن تطبيقات Windows الأصلية هي برامج يمكن تشغيلها في مرحلة مبكرة من تشغيل Windows. يستخدمون وظائف فقط من Ntdll. مثال على مثل هذا التطبيق:
autochk الذي ينفذ
أداة chkdisk لفحص القرص بحثًا عن الأخطاء قبل بدء الخدمات الرئيسية. في هذا المستوى نريد أن نرى استعادة نشطة لدينا.
ماذا نحتاج؟
- DDK (مجموعة تطوير برامج التشغيل) ، المعروفة الآن باسم WDK 7 (مجموعة برامج تشغيل Windows).
- آلة افتراضية (مثل windows 7 x64)
- ليس بالضرورة ، ولكن يمكن تنزيل ملفات الرأس هنا.
ما هو في الكود؟
لنمارس بعض الشيء ، وعلى سبيل المثال سنكتب طلبًا صغيرًا:
- يعرض رسالة على الشاشة.
- يخصص القليل من الذاكرة
- في انتظار إدخال لوحة المفاتيح
- تحرر ذاكرة مشغول
في التطبيقات الأصلية ، ليست نقطة الإدخال هي العنصر الرئيسي أو نظام التشغيل الرئيسي ، ولكن وظيفة NtProcessStartup ، لأننا نبدأ بالفعل العملية الجديدة مباشرة في النظام.
لنبدأ بعرض الرسالة على الشاشة. للقيام بذلك ، لدينا دالة أصلية
NtDisplayString ، والتي تأخذ كوسيطة مؤشر إلى كائن بنية UNICODE_STRING. سوف RtlInitUnicodeString مساعدتنا في التهيئة. نتيجة لذلك ، لعرض النص على الشاشة ، يمكننا كتابة مثل هذه الوظيفة الصغيرة:
نظرًا لأن وظائف ntdll متاحة فقط لنا ، ولا توجد مكتبات أخرى في الذاكرة بعد ، فبالتأكيد سنواجه مشكلات في كيفية تخصيص الذاكرة. المشغل الجديد غير موجود حتى الآن (لأنه يأتي من عالم C ++ عالي المستوى) ، كما لا توجد وظيفة malloc (يحتاج إلى مكتبات وقت التشغيل C). يمكنك بالطبع استخدام فقط المكدس. ولكن إذا كنا بحاجة إلى تخصيص ذاكرة بشكل حيوي ، فسيتعين علينا القيام بذلك على الكومة (أي الكومة). لذلك ، دعونا ننشئ حفنة لأنفسنا وسنتخذ منها ذاكرة عندما نحتاج إليها.
وظيفة
RtlCreateHeap مناسبة لهذه المهمة. علاوة على ذلك ، باستخدام RtlAllocateHeap و RtlFreeHeap ، سنحتل الذاكرة الخالية عندما نحتاج إليها.
PVOID memory = NULL; PVOID buffer = NULL; ULONG bufferSize = 42;
دعنا ننتقل إلى انتظار إدخال لوحة المفاتيح.
كل ما نحتاج إليه هو استخدام
NtReadFile على جهاز مفتوح ، وانتظر حتى ترجع لوحة المفاتيح إلينا. في حالة الضغط على مفتاح ESC ، سنواصل العمل. لفتح الجهاز ، نحتاج إلى استدعاء وظيفة NtCreateFile (ستحتاج إلى فتح \ Device \ KeyboardClass0). سوف نتصل أيضًا بـ
NtCreateEvent لتهيئة الكائن للانتظار. سنعلن بشكل مستقل عن بنية KEYBOARD_INPUT_DATA تمثل بيانات لوحة المفاتيح. هذا سيسهل عملنا.
ينتهي التطبيق الأصلي بدعوة إلى الدالة
NtTerminateProcess ، لأننا نقتل عمليتنا فقط.
كل رمز تطبيق صغير لدينا:
#include "ntifs.h"
PS: يمكننا بسهولة استخدام الدالة DbgBreakPoint () في التعليمات البرمجية لإيقاف في المصحح. صحيح ، سوف تحتاج إلى توصيل WinDbg إلى الجهاز الظاهري لتصحيح أخطاء kernel. يمكن العثور على تعليمات حول كيفية القيام بذلك
هنا أو فقط استخدام
VirtualKD .
تجميع والتجمع
أسهل طريقة لإنشاء تطبيق أصلي هي استخدام
DDK (مجموعة تطوير برامج التشغيل). نحتاج إلى الإصدار السابع القديم تمامًا ، نظرًا لأن الإصدارات الأحدث لها نهج مختلف قليلاً وتعمل عن قرب مع Visual Studio. إذا استخدمنا DDK ، فإن مشروعنا يحتاج فقط إلى Makefile والمصادر.
ماكيفيلي !INCLUDE $(NTMAKEENV)\makefile.def
مصادر: TARGETNAME = MyNative TARGETTYPE = PROGRAM UMTYPE = nt BUFFER_OVERFLOW_CHECKS = 0 MINWIN_SDK_LIB_PATH = $(SDK_LIB_PATH) SOURCES = source.c INCLUDES = $(DDK_INC_PATH); \ C:\WinDDK\7600.16385.1\ndk; TARGETLIBS = $(DDK_LIB_PATH)\ntdll.lib \ $(DDK_LIB_PATH)\nt.lib USE_NTDLL = 1
سيكون Makefile الخاص بك هو نفسه تمامًا ، ولكن دعنا نتعمق في المصادر بمزيد من التفاصيل. يحتوي هذا الملف على مصادر البرنامج (ملفات .c) وخيارات الإنشاء والمعلمات الأخرى.
- TARGETNAME - اسم الملف القابل للتنفيذ ، والذي يجب أن يكون النتيجة.
- TARGETTYPE - نوع الملف القابل للتنفيذ ، يمكن أن يكون برنامج تشغيل (.sys) ، ثم يجب أن تكون قيمة الحقل هي DRIVER ، إذا كانت المكتبة (.lib) ، فإن القيمة هي LIBRARY. في حالتنا ، نحتاج إلى ملف قابل للتنفيذ (.exe) ، لذلك قمنا بتعيين القيمة إلى PROGRAM.
- UMTYPE - القيم الممكنة لهذا الحقل: وحدة تحكم لتطبيق وحدة التحكم ، نوافذ للعمل في وضع الإطارات. لكننا نحتاج إلى تحديد الإقليم الشمالي للحصول على التطبيق الأصلي.
- BUFFER_OVERFLOW_CHECKS - التحقق من تكدس المخزن المؤقت لتجاوز سعة المخزن المؤقت ، وللأسف ليس في حالتنا ، أوقف تشغيله.
- MINWIN_SDK_LIB_PATH - تشير هذه القيمة إلى المتغير SDK_LIB_PATH ، لا تقلق من أنك لم تعلن عن مثل هذا المتغير في النظام ، وفي اللحظة التي ندير فيها عملية الإنشاء المحددة من DDK ، سيتم الإعلان عن هذا المتغير وسيشير إلى المكتبات اللازمة.
- المصادر - قائمة المصادر لبرنامجك.
- يتضمن - ملفات رأس اللازمة للتجميع. تشير عادةً إلى المسار إلى الملفات المرفقة مع DDK ، ولكن يمكنك تحديد أي ملفات أخرى بشكل اختياري.
- TARGETLIBS - قائمة المكتبات التي تحتاج إلى ربط.
- USE_NTDLL هو حقل مطلوب يجب تعيينه على الموضع 1. لأسباب واضحة.
- USER_C_FLAGS - أي إشارات يمكنك استخدامها في توجيهات المعالج الأولي عند إعداد رمز التطبيق.
للبناء ، نحتاج إلى تشغيل الإصدار x86 (أو x64) تم التحقق منه ، وتغيير دليل العمل إلى مجلد المشروع وتنفيذ الأمر Build. توضح النتيجة في لقطة الشاشة أننا جمعنا ملفًا قابلاً للتنفيذ.

لا يمكن تشغيل هذا الملف بكل بساطة ، حيث يقسم النظام ويرسلنا للتفكير في سلوكه بسبب الخطأ التالي:
كيفية تشغيل تطبيق أصلي؟
في بداية autochk ، يتم تحديد تسلسل بدء تشغيل البرامج حسب قيمة مفتاح التسجيل:
HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute
يقوم مدير الجلسة بتنفيذ البرامج من هذه القائمة واحدة تلو الأخرى. يبحث مدير الجلسة نفسه عن الملفات القابلة للتنفيذ في الدليل system32. تنسيق قيمة مفتاح التسجيل كما يلي:
autocheck autochk *MyNative
يجب أن تكون القيمة بالتنسيق الست عشري ، وليس بالتنسيق ASCII المعتاد ، لذلك ، سيكون المفتاح المعروض أعلاه بالتنسيق:
61,75,74,6f,63,68,65,63,6b,20,61,75,74,6f,63,68,6b,20,2a,00,4d,79,4e,61,74,69,76,65,00,00
لتحويل الاسم ، يمكنك استخدام خدمة عبر الإنترنت ، على سبيل المثال ،
هذه الخدمة .
اتضح أنه لتشغيل التطبيق الأصلي ، نحتاج إلى:
- نسخ الملف القابل للتنفيذ إلى مجلد system32
- إضافة مفتاح إلى التسجيل
- إعادة تشغيل الجهاز
للراحة ، إليك نص برمجي جاهز لتثبيت تطبيق أصلي:
install.bat @echo off copy MyNative.exe %systemroot%\system32\. regedit /s add.reg echo Native Example Installed pause
add.reg REGEDIT4 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager] "BootExecute"=hex(7):61,75,74,6f,63,68,65,63,6b,20,61,75,74,6f,63,68,6b,20,2a,00,4d,79,4e,61,74,69,76,65,00,00
بعد التثبيت وإعادة التشغيل ، حتى قبل ظهور شاشة اختيار المستخدم ، نحصل على الصورة التالية:

يؤدي
باستخدام مثال هذا التطبيق الصغير ، كنا مقتنعين أنه من الممكن تمامًا تشغيل التطبيق على مستوى Windows الأصلي. علاوة على ذلك ، سيستمر شباب جامعة Innopolis في إنشاء خدمة من شأنها أن تبدأ عملية التفاعل مع السائق في وقت أبكر بكثير من الإصدار السابق من مشروعنا. ومع ظهور غلاف win32 ، سيكون من المنطقي نقل التحكم إلى خدمة كاملة تم تطويرها بالفعل (المزيد حول هذا
هنا ).
في المقالة التالية ، سنتطرق إلى مكون آخر من خدمة Active Restore ، وهي برنامج تشغيل UEFI. اشترك في بلوق لدينا لا تفوت وظيفة المقبل.