آليّة حلّ أسماء DLL في Windows - ترتيب البحث وSxS

· آخر تحديث: · · Windows, DLL, المحمّل, الأمن, تطوير Windows

سجل التعديلات (3 تحديثات، آخر تحديث 3 Sep، 2026)

سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.

أُضيفت روابط الاستشارة الموجودة في الأصل الياباني (consultation_services). ولم يتغيّر نصّ المقالة نفسه. قراءة النسخة السابقة لهذا التحديث (DOI: 10.5281/zenodo.22240891)
أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621454)

هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.

小村 豪 (2026). آليّة حلّ أسماء DLL في Windows - ترتيب البحث وSxS. شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621454 https://comcomponent.com/ar/blog/2026/03/24/002-windows-dll-name-resolution/

DOI (أحدث نسخة)
10.5281/zenodo.21621454
DOI (هذه النسخة)
10.5281/zenodo.22279890

حين يصير الحديث عن التعامل مع DLL أصلي في Windows، يتكرّر كثيراً لبس من هذا النوع.

  • إذا كتبت LoadLibrary("foo.dll")، أين ينظر فعلاً
  • وضعتها في المجلّد نفسه مع الملفّ التنفيذي، فلماذا تُحمَّل DLL أخرى
  • أيّهما أولى، System32 أم مجلّد التطبيق
  • في أيّ مرحلة تفعل manifest وAPI set وKnown DLLs
  • ماذا يتغيّر باستخدام SetDllDirectory أو AddDllDirectory
  • بأيّ فعل يسهل هجوم زرع DLL أو DLL hijacking

هذا الحديث لا ينفع في العمل بمجرّد «حفظ ترتيب البحث في سطر واحد». في الواقع، محمّل Windows يقيّم أوّلاً عدّة قواعد خاصّة قبل أن يمسح نظام الملفّات بالترتيب.

لماذا لا يكفي حفظ ترتيب البحثمخطّط يبيّن أنّ حلّ أسماء DLL لا ينفع في العمل بحفظ ترتيب البحث في سطر واحد، وأنّ محمّل Windows يقيّم أوّلاً عدّة قواعد خاصّة قبل البحث المتتابع في نظام الملفّات.حفظ ترتيب البحث في سطر واحدلا ينفع في العملواقع محمّل Windowsتقييم القواعد الخاصّة أوّلاًثم البحث المتتابع في نظام الملفّات

الشكل 1: حلّ الأسماء يبدأ بقواعد خاصّة سابقة، قبل «بحث المجلّدات».

ترتّب هذه المقالة حلّ أسماء DLL في Windows عمليّاً، بما يشمل الفرق بين unpackaged app وpackaged app، وKnown DLLs، وloaded-module list، وAPI set، ومانيفست side-by-side، وأثر واجهات LoadLibraryEx. المحتوى يفترض معلومات Microsoft Learn العلنيّة في مارس 2026.123456789

مصطلحات هذه المقالة

نرتّب أوّلاً في سطر واحد كلمات ستظهر لاحقاً بلا شرح. التفصيل في فصول المتن.

المصطلح بجملة واحدة
packaged app / unpackaged app تطبيق يُوزَّع ويُثبَّت حزمة مثل MSIX، أم تطبيق يضع الملفّ التنفيذي في مجلّد كالسابق. تعريف ترتيب البحث نفسه مختلف (الفصلان 3 و4)
safe DLL search mode إعداد مفعّل افتراضيّاً ينقل current folder إلى خلف ترتيب البحث. يُعطَّل بجعل قيمة السجلّ SafeDllSearchMode صفراً1
DLL redirection آليّة تضع علامة اسم-التطبيق.exe.local بجانب الملفّ التنفيذي فيجعل المحمّل ينظر إلى مجلّد الملفّ التنفيذي أوّلاً. تُدعى أيضاً .local وDotLocal (الفصل 7)7
SxS (side-by-side) آليّة تُبقي إصدارات متعدّدة من DLL نفسها متعايشة، وتحدّد في manifest أيّ إصدار يُربَط. اختصار side-by-side، وقد يُشرح بالعربية بـ «التجاور» (الفصل 7)9
loaded-module list آليّة يتأكّد بها النظام مما إذا كانت DLL بالاسم نفسه محمّلة أصلاً في ذاكرة تلك العمليّة. بغضّ النظر عن المجلّد الذي قُرئت منه، إن كانت محمّلة استُخدمت (5.1)1
Known DLLs قائمة DLL يعدّها Windows معروفة في ذلك الإصدار. DLL المطابقة تُستخدم منها نسخة جهة النظام (5.2)1
API set اسم عقد مثل api-ms-win-.... اسم مستعار افتراضي يخفي DLL المادّيّة التي تنفّذ العقد (الفصل 6)3
package dependency graph مجموعة حزمة التطبيق نفسها وحزم التبعيّة المكتوبة PackageDependency في قسم Dependencies من مانيفست الحزمة. تُبحَث بترتيب كتابتها في المانيفست1

1. الخلاصة أوّلاً

نرتّب أوّلاً الخلاصة العمليّة وحدها.

  • حلّ أسماء DLL في Windows ليس «بحث نظام الملفّات أوّلاً». عناصر مثل DLL redirection وAPI set ومانيفست SxS وloaded-module list وKnown DLLs تدخل سابقة لترتيب البحث.1
  • في الشكل القياسي لـ unpackaged app مع تفعيل safe DLL search mode، مجلّد التطبيق في مرتبة عليا، لكن القواعد الخاصّة أعلاه تُقيَّم قبله.1
  • حتى إن حمّلت DLL بمسار كامل، لا تُثبَّت DLLات التبعيّة تلقائيّاً بالمسار الكامل نفسه. تُبحَث DLLات التبعيّة باسم الوحدة فقط، فقد تُحَلّ من موضع آخر.1
  • Known DLLs آليّة تربط DLL معيّنة يعدّها نظام التشغيل معروفة بنسخة جهة النظام، وليست حديثاً عن الكتابة فوقها بتوزيع جهة التطبيق العادي.1
  • API set ليس «اسم DLL المادّي نفسه»، بل اسماً مستعاراً افتراضيّاً يخفي DLL التنفيذ. النظر إلى اسم مثل api-ms-win-... بإحساس بحث DLL العادي يسهّل سوء الفهم.3
  • SetDllDirectory لا يغيّر ترتيب البحث فحسب، بل يملك سلوكاً يعطّل safe DLL search mode فعليّاً، فاستخدامه بخفّة قد ينعكس أثره أمنيّاً.1
  • في العمل، الأأمن تضييق نطاق البحث صراحة بالجمع بين المسار الكامل، وSetDefaultDllDirectories، وAddDllDirectory، وأعلام LOAD_LIBRARY_SEARCH_* في LoadLibraryEx.4562

باختصار، التفكير العملي أنّ حلّ أسماء DLL في Windows لا يُحسَم بـ «أيّ مجلّد في المرتبة كم»، بل بـ «أيّ قواعد سابقة تحلّ الاسم إلى ماذا» و«كيف غيّر الـ API فضاء البحث».

ثلاثة عناصر تحسم حلّ أسماء DLLمخطّط يبيّن أنّ حلّ أسماء DLL في Windows لا يُحسَم بترتيب المجلّدات أيّها في المرتبة كم فحسب، بل بتداخل ثلاثة عناصر: القواعد السابقة التي تحلّ الاسم إلى ماذا، وترتيب بحث المجلّدات، وكيف غيّر الـ API فضاء البحث.القواعد السابقة للاسمDLL التي تُحمَّل فعلاًترتيب بحث المجلّداتفضاء البحث الذي غيّره الـ APIredirection وKnown DLLsأعلام LoadLibraryEx

الشكل 2: نتيجة الحلّ تُحسَم بتداخل القواعد السابقة وترتيب المجلّدات والـ API.

في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 23، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle

2. لحلّ أسماء DLL قواعد سابقة قبل «بحث المجلّدات»

في شرح DLL search order في Microsoft Learn، تُعامَل عند تحميل DLL أوّلاً عناصر كهذه جزءاً من ترتيب البحث.1

  1. DLL redirection
  2. API sets
  3. SxS manifest redirection
  4. loaded-module list
  5. Known DLLs

بعد ذلك يدخل البحث في نظام الملفّات مثل مجلّد التطبيق وSystem32 ومجلّد Windows وPATH.1

إن أُغفل هذا، بدا مثلاً «أن يُحسَم شيء قبل مجلّد التطبيق غريباً»، لكن في شرح محمّل Windows ذلك هو المسار الأصليّ بالأحرى.

نريد حلّ اسم DLLDLL redirectionAPI setSxS manifest redirectionloaded-module listKnown DLLsترتيب البحث في نظام الملفّاتتُحسَم DLL التي تُحمَّل فعلاً

الشكل 3: تُقيَّم القواعد السابقة بالترتيب، ولا يدخل بحث نظام الملفّات إلّا إن لم يطابق أيّ منها.

3. ترتيب البحث القياسي لـ unpackaged app

في تطبيق سطح مكتب عادي، عند تحميل DLL بلا مسار كامل، يشرح Microsoft Learn ترتيب البحث القياسي لـ unpackaged app. في الحالة الافتراضيّة مع تفعيل safe DLL search mode يكون الترتيب التالي.1

# موضع البحث النوع ملاحظة
1 DLL redirection قاعدة سابقة هل يوجد اسم-التطبيق.exe.local (الفصل 7)
2 API sets قاعدة سابقة من اسم العقد إلى DLL التنفيذ (الفصل 6)
3 SxS manifest redirection قاعدة سابقة الربط عبر manifest (الفصل 7)
4 loaded-module list قاعدة سابقة هل وحدة بالاسم نفسه محمّلة (5.1)
5 Known DLLs قاعدة سابقة إن طابقت فسخة جهة النظام (5.2)
6 package dependency graph قاعدة سابقة Windows 11 الإصدار 21H2 فصاعداً. تُبحَث بترتيب كتابتها في المانيفست
7 المجلّد الذي حُمِّل منه التطبيق نظام ملفّات من هنا يبدأ بحث المجلّدات الفعليّ
8 مجلّد النظام نظام ملفّات عادة %SystemRoot%\System32. الموضع الذي يمكن الحصول عليه بـ GetSystemDirectory
9 16-bit system folder نظام ملفّات مجلّد System من عصر 16 بتّاً. لا توجد دالّة للحصول على المسار، لكن البحث يتمّ. في التطبيقات الحديثة نادراً ما يُلتفَت إليه، ويكفي معرفة أنّه «باقٍ بنداً في الترتيب»
10 مجلّد Windows نظام ملفّات الموضع الذي يمكن الحصول عليه بـ GetWindowsDirectory
11 current folder نظام ملفّات إن عُطِّل safe DLL search mode صعد هذا إلى موضع 8
12 المجلّدات المصطفّة في PATH نظام ملفّات لا يشمل المسار الخاصّ بالتطبيق في مفتاح سجلّ App Paths

هذا الجدول ليس للحفظ. إنّه جدول لتحديد في أيّ درجة حُسمت حالتك. إن حاولت إصلاح مشكلة حُسمت في 1 إلى 6 بتوزيع المجلّدات من 7 فصاعداً، فلن يتغيّر شيء مهما فعلت.

الاستخدام الصحيح لجدول ترتيب البحثمخطّط يبيّن أنّ جدول ترتيب البحث ليس للحفظ بل لتحديد في أيّ درجة حُسمت حالتك، وأنّ محاولة إصلاح مشكلة حُسمت بالقواعد السابقة بتوزيع المجلّدات لا تغيّر شيئاً.قواعد سابقة (1 إلى 6)نظام الملفّات (7 إلى 12)العَرَض: تُقرأ DLL غير مقصودةفي أيّ درجة حُسم الأمرتغيير توزيع المجلّدات لا يغيّر شيئاًمجال ينفع فيه ترتيب التوزيع أو المسار

الشكل 4: قبل الإصلاح، حدّد في أيّ درجة حُسمت تلك المشكلة.

ما ينفع خصوصاً في العمل هذه الثلاث.

  • current folder في الخلف كثيراً افتراضيّاً. safe DLL search mode يصعّب تقديم current folder.1
  • لكن الوجود في الخلف لا يعني الأمان. ما دام مجلّد يستطيع المهاجم السيطرة عليه باقياً في أهداف البحث، بقيت فسحة DLL preloading.2
  • من Windows 11 21H2 فصاعداً دخل package dependency graph أيضاً في شرح بحث unpackaged app. فرق يسهل إغفاله إن حُفظ الشرح القديم وحده.1
موضع current folder والأمانمخطّط يبيّن أنّ current folder يُوضَع في خلف ترتيب البحث كثيراً في الافتراضي مع تفعيل safe DLL search mode، لكن الوجود في الخلف لا يعني الأمان، وما دام موضع يستطيع المهاجم السيطرة عليه باقياً في أهداف البحث بقيت فسحة DLL preloading.safe DLL search mode مفعّل (افتراضي)يُوضَع current folder في الخلف كثيراًالوجود في الخلف لا يعني الأمانإن بقي موضع يستطيع المهاجم السيطرة عليه في أهداف البحث بقيت الفسحة

الشكل 5: موضع current folder خُفِّض، لكن ذلك وحده لا يصير دفاعاً.

4. packaged app وunpackaged app ليسا سواء

في Microsoft Learn يُعرَّف لـ packaged app ترتيب بحث آخر. في packaged app يفعل package dependency graph في مرحلة أسبق، ويختلف تفكير البحث نفسه قليلاً.1

إن أُغفل هذا الفرق وقع لبس كهذا بعد التحويل إلى MSIX أو إدخال Windows App SDK.

  • DLL تُوجَد في تشغيل unpackaged أثناء التطوير ولا تُوجَد في حزمة الإنتاج
  • تختلط تبعية مانيفست الحزمة بالاعتماد القديم على PATH فتتغيّر شروط إعادة الإنتاج
  • يُشرح «ترتيب بحث DLL في Windows هكذا» بجدول واحد فيُسقط فرق سلوك packaged app

في المقالات ومراجعة التصميم، الأأمن الفصل أوّلاً: «أحديث packaged app أم unpackaged app؟».1

افصل packaged عن unpackaged أوّلاًمخطّط يبيّن أنّ لـ packaged app ترتيب بحث آخر ويفعل package dependency graph في مرحلة أسبق، فيقع لبس مثل DLL تُوجَد في تشغيل unpackaged أثناء التطوير ولا تُوجَد في حزمة الإنتاج، لذلك الأأمن في مراجعة التصميم الفصل أوّلاً عن أيّ الحديثين.unpackaged apppackaged appحديث أيّ تطبيقترتيب البحث القياسي في الفصل 3ترتيب بحث آخر (dependency graph سابق)منبع لبس تغيّر السلوك بعد MSIX عن وقت التطوير

الشكل 6: ترتيب البحث ليس جدولاً واحداً؛ ينقسم أوّلاً حسب packaged أو unpackaged.

5. ماذا يفعل Known DLLs وloaded-module list

ما يخالف الحدس في حلّ DLL هو loaded-module list وKnown DLLs.

5.1 loaded-module list

يشرح Microsoft Learn أنّ النظام يستطيع التأكّد مما إذا كانت DLL بالاسم نفسه محمّلة أصلاً في الذاكرة.1

أي إنّه قبل بحث نظام الملفّات يدخل حكم:

  • أذلك الاسم محمّل أصلاً
  • وبالتالي، هل ثمّة حاجة أصلاً للبحث الآن

لذلك إن أُغفلت أثناء التحقيق حقيقة أنّ «DLL بالاسم نفسه من مجلّد آخر كانت محمّلة أوّلاً في هذه العمليّة»، أُسيء قراءة شروط إعادة الإنتاج.

حكم loaded-module listمخطّط يبيّن أنّه قبل بحث نظام الملفّات يُتأكَّد مما إذا كانت DLL بالاسم نفسه محمّلة أصلاً في ذاكرة تلك العمليّة، فإن كانت محمّلة استُخدمت بغضّ النظر عن المجلّد الذي قُرئت منه، فتسقط الحاجة نفسها إلى البحث.محمّلةغير محمّلةطلب حلّ اسم DLLهل وحدة بالاسم نفسه محمّلةاستخدام تلك الوحدة (المجلّد غير ذي صلة)التقدّم إلى البحث التاليإن قُرئت أوّلاً DLL بالاسم نفسه من مجلّد آخر تِهت

الشكل 7: إن وُجدت وحدة محمّلة بالاسم نفسه، لا يُبحث من جديد.

5.2 Known DLLs

Known DLLs قائمة DLL يعدّها Windows معروفة في ذلك الإصدار، ويمكن تأكيدها في HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\KnownDLLs. إن طابقت DLL، استخدم النظام نسخة تلك DLL المعروفة.1

المهمّ هنا أنّ Known DLLs ليست حديثاً من نوع «يكفي أن يضع التطبيق العامّ DLL بالاسم نفسه في مجلّد التطبيق ليفوز». فهمها سباق أسبقيّة بسيطاً مع System32 يسيء فهم السلوك.

عمل Known DLLsمخطّط يبيّن أنّ Known DLLs قائمة DLL يعدّها Windows معروفة في ذلك الإصدار، فإن طابقت استُخدمت نسخة جهة النظام، لذلك ليست حديثاً من نوع أن يضع التطبيق العامّ DLL بالاسم نفسه في مجلّد التطبيق ليفوز، وفهمها سباق أسبقيّة مع System32 يسيء الفهم.يطابقلا يطابقاسم DLL المراد حلّههل يطابق Known DLLsتُستخدم نسخة جهة النظامالتقدّم إلى البحث التاليليس حديث كتابة فوق بتوزيع مجلّد التطبيق

الشكل 8: DLL المعروفة تُربَط بنسخة جهة النظام ولا تصير سباق أسبقيّة.

6. API set اسم عقد لا «اسم DLL مادّي»

حين ترى اسماً مثل api-ms-win-core-... يسهل التفكير «من أين نبحث عن ملفّ DLL ذلك». لكن Microsoft Learn يشرح أنّ API set اسم مستعار افتراضي لـ DLL مادّي، وآليّة تفصل التنفيذ عن العقد.3

أي إنّ التفكير:

  • اسم API set = اسم ملفّ DLL المادّي كما هو
  • حلّ API set = بحث ملفّات كـ DLL العادي

غير دقيق.

إذا أُدخل تفكير API set صار أسهل شرح أنّ:

  • الأمر يستقيم حتى إن اختلف اسم DLL التنفيذ حسب إصدار Windows أو نوع الجهاز
  • جهة الاستدعاء لا تحتاج معرفة ثابتة بـ «أيّ DLL مضيف ينفّذ»

3

API set اسم عقدمخطّط يبيّن أنّ اسماً مثل api-ms-win- ليس اسم ملفّ DLL مادّي بل اسماً مستعاراً افتراضيّاً يخفي DLL التنفيذ، وبفصل العقد عن التنفيذ يستقيم الأمر حتى إن اختلف التنفيذ حسب الإصدار أو الجهاز، ولا تحتاج جهة الاستدعاء معرفة ثابتة بأيّ DLL مضيف ينفّذ.اسم عقد api-ms-win-…يُحَلّ اسماً مستعاراً افتراضيّاًDLL المادّي الذي ينفّذ مخفيّيستقيم حتى إن اختلف التنفيذ حسب الإصدار أو الجهازإحساس بحث الملفّات العادي غير دقيق

الشكل 9: API set ليس «اسم ملفّ يُبحث عنه»، بل اسم عقد يخفي التنفيذ.

7. manifest وside-by-side (SxS) حلّ آخر لمشكلة إصدارات DLL

DLL redirection ومانيفست SxS ليسا حيلة صغيرة في ترتيب البحث، بل يُشرحان بوصفهما آليّة لتفادي تصادم إصدارات DLL.789

في Microsoft Learn الترتيب هو:

  • manifest ملفّ XML يصف side-by-side assembly أو isolated application
  • side-by-side assembly وحدة التسمية والربط والإصدار والتوزيع
  • المحمّل يحكم أيّ إصدار يُربَط بناء على التبعيّة المكتوبة في manifest

89

علاقة manifest وside-by-sideمخطّط يبيّن أنّ manifest ملفّ XML يصف side-by-side assembly أو isolated application، وأنّ side-by-side assembly وحدة التسمية والربط والإصدار والتوزيع، وأنّ المحمّل يحكم أيّ إصدار يُربَط بناء على التبعيّة المكتوبة في manifest، بوصف ذلك آليّة لتفادي تصادم إصدارات DLL.manifest (XML)وصف side-by-side assembly والإصدار المعتمدالمحمّل يحكم أيّ إصدار يُربَطيمكن تعايش إصدارات متعدّدة من DLL نفسها

الشكل 10: SxS ليس حيلة ترتيب بحث، بل حلّاً آخر بوصفه آليّة لتصادم الإصدارات.

لذلك يلزم في العمل فصل هذه الثلاثة.

  • حديث وضع private DLL في مجلّد التطبيق فحسب
  • حديث استخدام DLL redirection مثل .local
  • حديث استخدام ربط side-by-side عبر manifest

الثلاثة قريبة من جهة «تؤثّر في حلّ DLL»، لكن قصد التصميم ليس واحداً. يرشد Microsoft Learn إلى التمييز: إن أردت الحلّ دون لمس تطبيق قائم فـ DLL redirection، وإن كان تطبيقاً جديداً فمكوّن side-by-side.7

تمييز الوسائل الثلاثمخطّط يبيّن أنّ حديث وضع private DLL في مجلّد التطبيق، وحديث استخدام DLL redirection مثل .local، وحديث ربط side-by-side عبر manifest، تؤثّر كلّها في حلّ DLL لكن قصد التصميم ليس واحداً، ويُرشَد إلى DLL redirection حين تريد الإصلاح دون لمس تطبيق قائم، وside-by-side لتطبيق جديد.حديث أيّ وسيلةوضع private DLL في مجلّد التطبيقDLL redirection (.local)ربط مانيفست SxSحين تريد الإصلاح دون لمس تطبيق قائمإدارة تبعيّة تطبيق جديد

الشكل 11: وسائل ثلاث تبدو متشابهة تُميَّز بقصد التصميم.

7.1 ماذا يفعل .local تحديداً

كثيراً ما يُمرَّر في سطر، فنكتب سلوك unpackaged app مفصولاً.7

  • ما يُوضَع: اسم ملفّ إعادة التوجيه اسم-الملفّ-التنفيذي.local. إن كان Editor.exe فضع Editor.exe.local في المجلّد نفسه مع الملفّ التنفيذي. ضع DLL المراد تحميلها في المجلّد نفسه أيضاً
  • المحتوى: محتوى الملفّ يُتجاهَل. الوجود نفسه إشارة تجعل المحمّل ينظر إلى مجلّد الملفّ التنفيذي أوّلاً عند تحميل DLL
  • نطاق الفعل: يفعل في التحميل بمسار كامل وفي التحميل باسم الوحدة فقط. بغضّ النظر عن المسار الممرَّر إلى LoadLibrary أو LoadLibraryEx، إن وُجدت DLL بالاسم نفسه في مجلّد الملفّ التنفيذي قُرئت تلك. المواصفة لإنقاذ مواضع ليس فيها سوى موضع تسجيل واحد مثل COM
  • إن لم تُوجَد: إن لم تكن في مجلّد الملفّ التنفيذي عاد الأمر إلى ترتيب البحث العادي
  • شكل المجلّد: يعمل أيضاً بصنع مجلّد اسمه Editor.exe.local ووضع DLL داخله
  • التفعيل على الجهاز كلّه: اصنع قيمة DWORD اسمها DevOverrideEnable في HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options واجعلها 1 ثم أعد التشغيل. بهذا الإعداد تفعل إعادة التوجيه بـ .local حتى إن كان للتطبيق application manifest
  • في packaged app: يتغيّر موضع الوضع، ويُنظَر إلى <موضع تثبيت الحزمة>\microsoft.system.package.metadata\application.local\

هنا أثر جانبي إن أُغفل تاه التحقيق. إذا استخدمت DLL redirection وكان التطبيق لا يستطيع الوصول إلى كلّ الأقراص والمجلّدات في ترتيب البحث، أوقف LoadLibrary البحث لحظة رفض الوصول. إن لم تستخدم DLL redirection، قُفز عن المجلّدات غير القابلة للوصول واستمرّ البحث.7 إن قيل في بيئة وُضع فيها .local فقط «DLL التي يفترض أن تكون أبعد غير موجودة»، فاشتبه في هذا الفرق.

سلوك .localمخطّط يبيّن أنّ علامة اسم-الملفّ-التنفيذي.local في المجلّد نفسه مع الملفّ التنفيذي تجعل الوجود نفسه إشارة بغضّ النظر عن المحتوى فينظر إلى جانب الملفّ التنفيذي أوّلاً ويفعل حتى في التحميل بمسار كامل، وأنّه إن لم توجد عاد إلى ترتيب البحث العادي، مع أثر جانبي هو وقف البحث عند الرفض.توجدلا توجدوضع اسم-الملفّ-التنفيذي.localالنظر إلى جانب الملفّ التنفيذي أوّلاًهل توجد DLL بالاسم نفسهقراءتها حتى مع المسار الكاملالعودة إلى ترتيب البحث العاديأثر جانبي: وقف البحث عند الرفض

الشكل 12: .local إشارة بالوجود نفسه، ويفعل حتى في التحميل بمسار كامل.

8. ماذا يتغيّر بـ LoadLibraryEx وSetDllDirectory وAddDllDirectory

8.1 SetDllDirectory

يغيّر SetDllDirectory ترتيب البحث، لكن Microsoft Learn ينصّ صراحة على أنّه يعطّل safe DLL search mode فعليّاً.1

أي حتى إن استخدمته بنيّة «إضافة مجلّد واحد خاصّ بالتطبيق»، تغيّر فضاء البحث بما يشمل التعامل مع current folder.

ثمّ إن استُدعي SetDllDirectory في العمليّة الأب، فقد يصل الأثر إلى ترتيب البحث القياسي في جهة العمليّة الابن.1

لذلك في العمل، الأأمن من استعمال SetDllDirectory بخفّة الميل إلى:

  • SetDefaultDllDirectories
  • AddDllDirectory
  • LOAD_LIBRARY_SEARCH_* في LoadLibraryEx

456

فخّ SetDllDirectoryمخطّط يبيّن أنّ SetDllDirectory حتى بنيّة إضافة مجلّد واحد خاصّ بالتطبيق يعطّل safe DLL search mode فعليّاً فيغيّر فضاء البحث كلّه، وأنّ الاستدعاء في العمليّة الأب قد يصل أثره إلى ترتيب بحث العمليّة الابن، لذلك الأأمن الميل إلى واجهات مثل SetDefaultDllDirectories وAddDllDirectory وأعلام بحث LoadLibraryEx.إضافة مجلّد واحد بـ SetDllDirectoryتعطيل safe DLL search mode فعليّاًتغيّر فضاء البحث كلّه بما يشمل current folderقد يصل الأثر إلى ترتيب بحث العمليّة الابنالميل بدلاً من ذلك إلى واجهات مجموعة SetDefaultDllDirectories

الشكل 13: نيّة «إضافة مجلّد واحد فقط» تُضعِف فضاء البحث كلّه.

8.2 AddDllDirectory

المسارات المضافة بـ AddDllDirectory تُستخدم مع LOAD_LIBRARY_SEARCH_USER_DIRS. في Microsoft Learn، ترتيب البحث عند إضافة أكثر من واحد غير محدّد.15

لذلك فالأفضل تجنّب تصميم:

  • أضفت مجلّدات متعدّدة
  • وتوقّعت ترتيب استكشافها بدقّة أيضاً
استخدام AddDllDirectory والتنبيهمخطّط يبيّن أنّ المسارات المضافة بـ AddDllDirectory تفعل مع LOAD_LIBRARY_SEARCH_USER_DIRS، وأنّ ترتيب البحث عند إضافة أكثر من واحد غير محدّد، لذلك ينبغي تجنّب تصميم يضيف مجلّدات متعدّدة ويتوقّع ترتيب استكشافها بدقّة.إضافة مسار بـ AddDllDirectoryيفعل مع LOAD_LIBRARY_SEARCH_USER_DIRSالترتيب عند الإضافة المتعدّدة غير محدّدتجنّب تصميماً يعتمد على ترتيب الاستكشاف

الشكل 14: الترتيب بين مجلّدات المستخدم المضافة لا يُعوَّل عليه في المواصفة.

8.3 SetDefaultDllDirectories

يُشرح SetDefaultDllDirectories بوصفه واجهة تخرج من مسار بحث DLL القياسي المجلّدات الأسهل ضعفاً وتقيّد أهداف البحث.4

خصائص يُستحسن ضبطها خصوصاً هذه الثلاث.

  • يفعل على مستوى العمليّة
  • بعد الاستدعاء يستمرّ طوال عمر العمليّة
  • لا يمكن إعادة مسار البحث القياسي الذي ضُبط مرّة إلى شكله القياسي الأصلي كما هو

من جهة الأمن، واجهة يسهل بها تصميم «الميل فور الإقلاع إلى فضاء بحث أميل إلى الأمان».4

خصائص SetDefaultDllDirectoriesمخطّط يبيّن أنّ SetDefaultDllDirectories واجهة تخرج من مسار بحث DLL القياسي المجلّدات الأسهل ضعفاً وتقيّد أهداف البحث، وتفعل على مستوى العمليّة وتستمرّ طوال عمر العمليّة بعد الاستدعاء ولا يمكن إعادة مسار البحث الذي ضُبط مرّة إلى شكله القياسي الأصلي، لذلك يسهل بها تصميم الميل فور الإقلاع إلى فضاء بحث أميل إلى الأمان.SetDefaultDllDirectories فور الإقلاعإخراج المواضع الأسهل ضعفاً من أهداف البحثيفعل على مستوى العمليّة طوال العمرلا يمكن الإعادة إلى الشكل القياسي الأصلي

الشكل 15: واجهة تُستدعى مرّة فور الإقلاع لإمالة العمليّة كلّها إلى جهة الأمان.

8.4 LoadLibraryEx

يستطيع LoadLibraryEx تغيير سلوك البحث بأعلام مثل LOAD_WITH_ALTERED_SEARCH_PATH وLOAD_LIBRARY_SEARCH_*.61

عمليّاً، واجهة تسهل تلبية مطالب مثل:

  • إدراج مجلّد DLL مصدر التحميل أيضاً في أهداف الاستكشاف، بما يشمل DLLات التبعيّة
  • التضييق على مجلّد التطبيق وSystem32 ومجلّدات المستخدم المضافة صراحة فقط

قبل الكتابة ثمّة أربعة قيود يُستحسن ضبطها.6

القيد المحتوى
الوسيط الثاني hFile محجوز للمستقبل، ويجب تمرير NULL حتماً
عدم الجمع LOAD_WITH_ALTERED_SEARCH_PATH لا يُجمَع مع أيّ علم LOAD_LIBRARY_SEARCH_*
المسار الكامل لازم إن استخدمت LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR وجب أن يكون الوسيط الأوّل مساراً كاملاً
الترتيب عند التحديد المتعدّد يُبحَث بترتيب LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR ثم LOAD_LIBRARY_SEARCH_APPLICATION_DIR ثم LOAD_LIBRARY_SEARCH_USER_DIRS ثم LOAD_LIBRARY_SEARCH_SYSTEM32. غير أنّ الترتيب داخل USER_DIRS غير محدّد

إن حدّدت علماً واحداً حتى من LOAD_LIBRARY_SEARCH_*، لا يُستخدم مسار البحث القياسي أصلاً. أي إن مرّرت LOAD_LIBRARY_SEARCH_SYSTEM32 وحده، لم يُبحث مجلّد التطبيق. «التضييق» يعني ذلك.

تحديد أعلام البحث يلغي المسار القياسيمخطّط يبيّن معنى «التضييق»: إن حدّدت علماً واحداً حتى من مجموعة LOAD_LIBRARY_SEARCH_ لم يُستخدم مسار البحث القياسي أصلاً، فإن مرّرت LOAD_LIBRARY_SEARCH_SYSTEM32 وحده لم يُبحث مجلّد التطبيق.لم تحدّدحتّى علم واحدهل حدّدت علماً من مجموعة LOAD_LIBRARY_SEARCH_يُستخدم مسار البحث القياسيلا يُستخدم مسار البحث القياسي أصلاًيُستكشَف نطاق الأعلام المحدّدة فقطSYSTEM32 وحده لا يبحث مجلّد التطبيق

الشكل 16: العلم ليس «إضافة»، بل تحديد «الاستبدال بذلك النطاق فقط».

8.5 أصغر مثال شيفرة (C/C++)

إذا جُمعت الواجهات حتى هنا في واحدة صارت على الشكل التالي. SetDefaultDllDirectories وLOAD_LIBRARY_SEARCH_* واجهتان من Windows 8 فصاعداً، لذلك يلزم التصريح بإصدار الهدف في الترويسة.46

/* cl /W4 loader.c  (Visual Studio 2022 + Windows SDK 10)
 * SetDefaultDllDirectories / AddDllDirectory / LOAD_LIBRARY_SEARCH_* は
 * Windows 8 以降。Windows 7 も対象にするなら KB2533623 が前提になり、
 * GetProcAddress で Kernel32.dll から実行時に取得する形が必要です。 */
#define _WIN32_WINNT 0x0602   /* Windows 8 */
#include <windows.h>
#include <stdio.h>

int wmain(void)
{
    /* 1. プロセス既定の検索空間を安全側へ寄せる。
     *    current folder と PATH を検索対象から外すのが目的です。
     *    LOAD_LIBRARY_SEARCH_DEFAULT_DIRS は
     *    APPLICATION_DIR + SYSTEM32 + USER_DIRS の組み合わせです。
     *    USER_DIRS を含めておかないと、手順 2 の AddDllDirectory は
     *    プロセス既定には反映されません。 */
    if (!SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)) {
        wprintf(L"SetDefaultDllDirectories failed: %lu\n", GetLastError());
        return 1;
    }

    /* 2. 自前のプラグインフォルダだけを明示的に足す。
     *    AddDllDirectory に渡すのは絶対パスでなければなりません。 */
    const wchar_t *pluginDir = L"C:\\MyApp\\plugins";
    DLL_DIRECTORY_COOKIE cookie = AddDllDirectory(pluginDir);
    if (cookie == NULL) {
        wprintf(L"AddDllDirectory failed: %lu\n", GetLastError());
        return 1;
    }

    /* 3. ロードする。
     *    第 2 引数 hFile は予約済みなので必ず NULL。
     *    ここでフラグを渡すと、手順 1 のプロセス既定ではなく
     *    このフラグの組み合わせだけが使われます。
     *    APPLICATION_DIR を外しているので、アプリフォルダは探されません。 */
    HMODULE h = LoadLibraryExW(
        L"foo.dll",
        NULL,
        LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_SYSTEM32);
    if (h == NULL) {
        wprintf(L"LoadLibraryExW failed: %lu\n", GetLastError());
        RemoveDllDirectory(cookie);
        return 1;
    }

    /* 4. どこから読まれたかを必ず確認する。
     *    調査では「読めたかどうか」より「どこから読めたか」が重要です。 */
    {
        wchar_t loadedPath[MAX_PATH];
        DWORD cap = (DWORD)(sizeof(loadedPath) / sizeof(loadedPath[0]));
        DWORD len = GetModuleFileNameW(h, loadedPath, cap);
        if (len > 0 && len < cap) {
            wprintf(L"loaded from: %s\n", loadedPath);
        } else {
            wprintf(L"GetModuleFileNameW failed: %lu\n", GetLastError());
        }
    }

    FreeLibrary(h);
    RemoveDllDirectory(cookie);
    return 0;
}

ما تضبطه هذه الشيفرة أربع نقاط.

  1. استدعِ SetDefaultDllDirectories أوّلاً. بعد الاستدعاء مرّة يفعل طوال عمر العمليّة، ولا يمكن الإعادة إلى مسار البحث القياسي4
  2. AddDllDirectory لا معنى له إلّا مع LOAD_LIBRARY_SEARCH_USER_DIRS. إن لم يتضمّن SetDefaultDllDirectories قيمة USER_DIRS، لم تُستخدم المجلّدات المضافة إلّا في استدعاء LoadLibraryEx الذي حدّد LOAD_LIBRARY_SEARCH_USER_DIRS5
  3. قم بالتنظيف. الـ cookie الذي أعاده AddDllDirectory يمكن إزالته بتمريره إلى RemoveDllDirectory5
  4. أخرج موضع القراءة. وجود سطر GetModuleFileNameW أو غيابه يغيّر زمن التحقيق عند العطل

لاحظ أنّ DLL التي يعتمد عليها foo.dll أيضاً تُبحَث في نطاق LOAD_LIBRARY_SEARCH_* هذا. إن أردت مجلّد foo.dll نفسه هدفاً لاستكشاف DLLات التبعيّة، فاجعل الوسيط الأوّل مساراً كاملاً وأضف LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR.

تدفّق أصغر مثال شيفرةمخطّط يبيّن التدفّق: إمالة فضاء البحث الافتراضي للعمليّة إلى جهة الأمان بـ SetDefaultDllDirectories، وإضافة مجلّد الإضافة صراحة بـ AddDllDirectory، والتحميل بتحديد أعلام البحث في LoadLibraryEx، وتأكيد موضع القراءة بـ GetModuleFileNameW، ثم التنظيف بـ RemoveDllDirectory.1. إمالة الافتراضي إلى الأمان بـ SetDefaultDllDirectories2. إضافة المجلّد المسموح بـ AddDllDirectory3. تحميل بتحديد الأعلام في LoadLibraryEx4. تأكيد مصدر التحميل بـ GetModuleFileNameW5. التنظيف بـ RemoveDllDirectory

الشكل 17: هيكل مثال الشيفرة خمس حركات: ضيّق ثم أضف ثم حمّل ثم أكّد ثم نظّف.

8.6 عند الاستخدام من C#

الفكرة نفسها تُستخدم في .NET أيضاً. مسار بحث P/Invoke يُتحكَّم فيه بسمة DefaultDllImportSearchPaths، والتحميل الصريح بـ NativeLibrary.Load.

// .NET 8 / C# 12
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;

// アセンブリ内のすべての P/Invoke について、既定の検索先を System32 に限定する。
// 制約: 絶対パスを指定した P/Invoke には、この属性は適用されません。
//       また Windows 以外のプラットフォームでは効果がありません。
[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.System32)]

internal static class Program
{
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern uint GetTickCount();

    private static void Main()
    {
        // السمة لا تفعل شيئاً بمجرد الإعلان. لا تسري إلا عند الاستدعاء.
        Console.WriteLine($"GetTickCount = {GetTickCount()}");

        // الملحق موضوع في مجلد مخصّص. لا تكتب هنا
        //   NativeLibrary.Load("foo.dll", asm, ApplicationDirectory | System32)
        // ApplicationDirectory يشير إلى مجلد الـ exe، وSystem32 إلى مجلد نظام التشغيل،
        // ولا ينظر أيّ منهما إلى مجلد الملحقات.
        // يفشل التحميل أو يمسك foo.dll آخر وُجد صدفة بجانب الـ exe.
        // إن عُرف موضع الملف، فالتحديد بمسار كامل هو الأضمن.
        string pluginDir = Path.Combine(AppContext.BaseDirectory, "plugins");
        string pluginPath = Path.GetFullPath(Path.Combine(pluginDir, "foo.dll"));
        if (!File.Exists(pluginPath))
        {
            throw new FileNotFoundException("الملحق غير موجود.", pluginPath);
        }

        // التحميل الزائد الذي يأخذ مساراً يقرأ ذلك الملف مباشرة (لا يبحث).
        IntPtr handle = NativeLibrary.Load(pluginPath);

        try
        {
            // تحقّق من أين حُمِّل.

            foreach (ProcessModule module in Process.GetCurrentProcess().Modules)
            {
                if (string.Equals(module.ModuleName, "foo.dll", StringComparison.OrdinalIgnoreCase))
                {
                    Console.WriteLine($"loaded from: {module.FileName}");
                }
            }
        }
        finally
        {
            NativeLibrary.Free(handle);
        }
    }
}

قيم DllImportSearchPath تقابل أعلام LOAD_LIBRARY_SEARCH_*. لذلك إن كتبت في جهة C# «System32 فقط» انطبق قيد 8.4 كما هو. لا يُبحث مجلّد التطبيق.

ما يُلتفَت إليه هنا أنّ DllImportSearchPath ليس فيه قيمة تشير إلى «مجلّد اختياري». ApplicationDirectory يشير إلى مجلّد الـ exe، وSystem32 إلى مجلّد نظام التشغيل، ولا توجد قيمة تمثّل مجلّداً قرّرته أنت للإضافة. لذلك إن حاولت التقاط ما وُضع في مجلّد مخصّص بأعلام البحث لم يصل، وصار التحديد بمسار كامل كما أعلاه.

لاحظ أيضاً أنّ القراءة بمسار كامل، كما في الفصل 9، لا تثبّت DLLات التي يعتمد عليها foo.dll. إن جاءت الإضافة بـ DLL تبعيّة خاصّة بها، لزم أسلوب جهة C كما هو: إضافة ذلك المجلّد بـ AddDllDirectory وتفعيل LOAD_LIBRARY_SEARCH_USER_DIRS، أو جمع التبعيّات في مجلّد واحد واستخدام LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR.

تفكير قراءة DLL في مجلّد مخصّص من C#مخطّط يبيّن أنّ DllImportSearchPath ليس فيه قيمة تشير إلى مجلّد اختياري، وأنّ ApplicationDirectory يشير إلى مجلّد الـ exe وSystem32 إلى مجلّد نظام التشغيل، لذلك لا تصل إضافة وُضعت في مجلّد مخصّص بأعلام البحث، فيُحدَّد مسار كامل ويُقرأ مباشرة، والتحكّم في DLLات التبعيّة يحتاج أسلوب جهة C كما هو.لا توجد قيمة تشير إلى مجلّد اختياريالطريقة الأكيدةوضع الإضافة في مجلّد مخصّصهل تُلتقَط بأعلام البحثالأعلام لا تصلتجميع مسار كامل والقراءة المباشرة بـ NativeLibrary.Loadالتحكّم في DLLات التبعيّة يحتاج أسلوب جهة C

الشكل 18: في .NET أيضاً، المجلّد الذي تقرّره بنفسك يُقرأ بمسار كامل على وجه أكيد.

9. المسار الكامل لا يثبّت DLLات التبعيّة

هذا موضع مهمّ جدّاً عمليّاً في هذا الموضوع ويسهل إغفاله. يشرح Microsoft Learn أنّه حتى إن حمّلت أوّل DLL بمسار كامل، تُبحَث DLLات التبعيّة لذلك الـ DLL باسم الوحدة فقط.14

أي إنّه ليس بالضرورة أنّ:

  • حمّلت C:\\MyApp\\plugins\\foo.dll صراحة
  • لذلك تُؤخَذ bar.dll التي يعتمد عليها foo.dll حتماً من المجلّد نفسه

إن وُجد هذا الوهم صار عطلاً مزعجاً قليلاً من نوع:

  • يعمل في بيئة التطوير
  • تُحَلّ bar.dll أخرى في جهة التوزيع
  • يصير تصادم DLL التبعيّة معتمداً على بيئة إعادة الإنتاج
المسار الكامل لا يثبّت DLLات التبعيّةمخطّط يبيّن أنّ تحميل أوّل DLL بمسار كامل يجعل ذلك الـ DLL نفسه يُقرأ كما حُدِّد، لكن DLL التي يعتمد عليها تُبحَث باسم الوحدة فقط فقد تُحَلّ من موضع آخر حسب البيئة، فيصير عطلاً معتمداً على البيئة يعمل في التطوير ويُكسر في جهة التوزيع.تحميل foo.dll بمسار كاملfoo.dll نفسه يُقرأ كما حُدِّدbar.dll المعتمدة تُبحَث باسم الوحدة فقطقد تُحَلّ من موضع آخر حسب البيئةعطل معتمد على البيئة يعمل في التطوير ويُكسر في جهة التوزيع

الشكل 19: ما يمكن تثبيته بمسار كامل هو الأوّل وحده، والتبعيّة معاملة أخرى.

10. كيف نتجنّب DLL preloading / hijacking

في أمن DLL في Microsoft Learn يُشرح أنّ الجمع بين التحميل الديناميكي بلا مسار كامل ومجلّد بحث يستطيع المهاجم السيطرة عليه يؤدّي إلى هجوم DLL preloading أو هجوم binary planting.2

في العمل يسهل التفكير بالميل إلى هذا الشكل الأساس.

  • تقليل التحميل باسم عارٍ مثل LoadLibrary("foo.dll")
  • استخدام المسار الكامل إن لزم
  • تضييق مسار البحث الافتراضي للعمليّة بـ SetDefaultDllDirectories
  • إضافة المجلّدات المسموح بها صراحة فقط بـ AddDllDirectory
  • التصريح بنطاق الاستكشاف بـ LOAD_LIBRARY_SEARCH_SYSTEM32 وLOAD_LIBRARY_SEARCH_APPLICATION_DIR وLOAD_LIBRARY_SEARCH_USER_DIRS وغيرها
  • تجنّب current folder والاعتماد غير الحذر على PATH

خصوصاً خطر أن تحمل عمليّة تعمل بامتياز المدير مسار بحث غامضاً. ويشرح Microsoft Learn أيضاً أنّ DLL خبيثة إذا حُمِّلت نُفِّذت بامتياز تلك العمليّة.2

الجمع الذي يقوم عليه هجوم DLL preloadingمخطّط يبيّن أنّ الجمع بين التحميل الديناميكي بلا مسار كامل ومجلّد بحث يستطيع المهاجم السيطرة عليه يكتمل شرط قيام هجوم DLL preloading، وأنّ DLL خبيثة إذا حُمِّلت نُفِّذت بامتياز تلك العمليّة، لذلك فالتدبير تقليل الأسماء العارية وتضييق فضاء البحث.تحميل ديناميكي بلا مسار كاملاكتمال شرط قيام DLL preloadingمجلّد بحث يستطيع المهاجم السيطرة عليهDLL خبيثة تُنفَّذ بامتياز العمليّةالتدبير: تقليل الأسماء العارية وتضييق فضاء البحث

الشكل 20: الهجوم يقوم بضرب «اسم غامض» في «موضع يمكن السيطرة عليه».

11. تأكيد أيّ DLL قُرئت من أين

حتى هنا كان حديث المواصفة. في التحقيق الفعليّ، التأكيد دون تخمين أسرع. نورد أربعة حسب الغرض.

ماذا تريد معرفته ما يُستخدم أين يُنظَر
أين بُحث وأين وُجد Process Monitor10 سجلّ الوصول إلى الملفّات
ما المحمّل الآن Process Explorer، tasklist /m11 قائمة وحدات العمليّة
أيّ عمليّة تمسك DLL معيّنة ListDLLs12 قائمة عبر العمليّات
المسار الذي قُرئ فعلاً أثناء التنقيح نافذة الوحدات في Visual Studio «تصحيح» > «نوافذ» > «وحدات»7

11.1 تتبع أثر الاستكشاف بـ Process Monitor

هذا الأغزر معلومات. الإجراء كالتالي.10

  1. شغّل Process Monitor مديراً وابدأ التسجيل
  2. افتح «Filter» > «Filter…» وأضف Process Name is مع اسم الـ exe المستهدف
  3. في الشاشة نفسها أضف Path ends with .dll
  4. شغّل التطبيق المستهدف وأوقف التسجيل عند وقوع المشكلة

ما يُنظَر إليه هنا عمود Result. يحاول المحمّل الفتح من الأعلى وفق ترتيب البحث، فتُصفّ NAME NOT FOUND في المواضع التي لم توجد، وSUCCESS في الموضع الذي فُتح فعلاً. أي إنّ السطر التالي لآخر صفّ من NAME NOT FOUND هو المسار المعتمد فعلاً.

بالمقابلة مع جدول الفصل 3 يتّضح في أيّ درجة حُسمت تلك الحالة. إن حُسمت بقواعد سابقة (1 إلى 6 في الجدول) لم يظهر أصلاً سجلّ الذهاب للبحث عن الملفّ. وهذا أيضاً دليل مهمّ.

كيف يُقرأ سجلّ Process Monitorمخطّط يبيّن أنّ المحمّل يحاول الفتح من الأعلى وفق ترتيب البحث فتصفّ NAME NOT FOUND في المواضع التي لم توجد وSUCCESS في الموضع الذي فُتح فعلاً، وأنّ السطر التالي لآخر صفّ من NAME NOT FOUND هو المسار المعتمد فعلاً، وأنّ غياب السجلّ نفسه يعني أنّ الأمر حُسم بقواعد سابقة.اقرأ عمود Result من الأعلىصفّ NAME NOT FOUND = مواضع بُحثت ولم توجدSUCCESS التالي هو المسار المعتمد فعلاًإن لم يظهر سجلّ فالأمر حُسم بقواعد سابقة

الشكل 21: الأثر يبقى في عمود Result، وغياب السجلّ نفسه دليل أيضاً.

11.2 إدراج الوحدات المحمّلة

إن أردت فقط رؤية ما قُرئ من أين في عمليّة شغّالة أصلاً، يمكن التأكيد بلا أداة إضافيّة.

tasklist /m foo.dll

هذا الأمر يدرج أسماء العمليّات وPID التي تحمّل الوحدة المحدّدة.11 بعد تضييق أيّ عمليّة هي المستهدفة، حوّل الجزء السفلي في Process Explorer إلى عرض DLL لتأكيد المسار الكامل لـ DLL التي تقرأها تلك العمليّة.

باستخدام ListDLLs من Sysinternals يمكن تأكيد الشيء نفسه من سطر الأوامر، وعبر العمليّات أيضاً.12

هل يفعل loaded-module list في 5.1 لا يُعرَف إلّا بهذه الطريقة. لأنّه إن حُمِّلت DLL بالاسم نفسه أوّلاً من مجلّد آخر، لم تبحث تلك العمليّة من جديد.

إجراء تأكيد الوحدات المحمّلةمخطّط يبيّن إجراء تضييق العمليّات التي تمسك الوحدة بـ tasklist /m ثم رؤية المسار الكامل بعرض DLL في Process Explorer أو ListDLLs، وأنّ أثر loaded-module list لا يُرى إلّا بهذه الطريقة.ضيّق العمليّات الممسكة بـ tasklist /m foo.dllانظر المسار الكامل في Process Explorer أو ListDLLsيُحسَم من أين قُرئت بوصفها واقعاًأثر loaded-module list لا يُرى إلّا هنا

الشكل 22: ما يُقرأ الآن يُحسَم بقائمة الوحدات، لا بالتخمين.

12. قائمة تحقّق للحكم في العمل

عند مراجعة تصميم تحميل DLL في Windows، يقلّ الحادث إن أكّدت على الأقلّ ما يلي.

  1. أذلك التطبيق packaged app أم unpackaged app
  2. أيّ DLL من الربط الساكن وأيّها تحميل ديناميكي
  3. أمسار كامل أم اسم وحدة فقط
  4. هل يُستخدم SetDllDirectory
  5. هل التكوين يتيح SetDefaultDllDirectories وLOAD_LIBRARY_SEARCH_*
  6. هل تُستخدم AddDllDirectory عدّة مرّات مع توقّع ضمني للترتيب
  7. بأيّ من manifest / SxS / private DLL / redirection تُدار التبعيّة
  8. هل ثمّة فرضيّة ضعيفة أمنيّاً على current folder أو PATH
  9. هل قد تُحَلّ DLL التبعيّة من موضع آخر في بيئة أخرى
  10. هل أكّدت في البيئة التي يظهر فيها العَرَض من أين قُرئت فعلاً (الفصل 11)

إن أكّدت هذه العشر مفصولة، أمكن إغلاق مشاكل مثل «DLL غير موجودة» و«قُرئت DLL مختلفة» و«لا يقلع في الإنتاج فقط» و«يتوقّف في مراجعة الثغرات» في مرحلة أسبق كثيراً.

تدفّق التأكيد في المراجعةمخطّط يبيّن أنّ حوادث مراجعة تصميم تحميل DLL تقلّ بالنظر بهذا التدفّق: شكل التطبيق packaged أو unpackaged، وطريقة التحميل مسار كامل أو اسم فقط، واستخدام الـ API مثل وجود SetDllDirectory والأعلام، وإدارة التبعيّة في manifest أو DLL التبعيّة، ثم تأكيد مصدر التحميل فعلاً في البيئة التي يظهر فيها العَرَض.تأكيد شكل التطبيق (packaged / unpackaged)تأكيد طريقة التحميل (مسار كامل أم اسم فقط)تأكيد استخدام الـ API (SetDllDirectory والأعلام)تأكيد إدارة التبعيّة (manifest / SxS / PATH)تأكيد مصدر التحميل في الميدان (الفصل 11)

الشكل 23: قائمة التحقّق تُهضَم بترتيب الشكل ثم التحميل ثم الـ API ثم التبعيّة ثم التأكيد الميداني.

13. خلاصة

حلّ أسماء DLL في Windows ليس مجرّد «ترتيب استكشاف المجلّدات». في الواقع يُحسَم بتداخل DLL redirection وAPI set ومانيفست SxS وloaded-module list وKnown DLLs وفضاء البحث الذي تغيّره استدعاءات الـ API.134

الأهمّ في العمل ينحصر في هذه الستّ.

  • لا تحفظ ترتيب البحث جدولاً واحداً. الجدول يُستخدم لتحديد في أيّ درجة حُسمت حالتك
  • افصل packaged / unpackaged
  • افهم أنّ المسار الكامل قد يجعل DLL التبعيّة معاملة أخرى
  • لا تستخدم SetDllDirectory بخفّة
  • إن ملت إلى جهة الأمان فاستخدم SetDefaultDllDirectories وأعلام بحث LoadLibraryEx
  • لا تنتهِ عند التخمين؛ أكّد المسار الذي قُرئ فعلاً بـ Process Monitor وغيره

حلّ اسم DLL موضع يسهل أن تظهر فيه مجتمعة أعطال الإقلاع وفروق البيئة ومشاكل الأمن. لذلك يستحقّ الفهم لا لـ «بأيّ ترتيب يبحث Windows» فحسب، بل لـ «ماذا يعامل Windows أصلاً فرضيّة لحلّ الأسماء» أيضاً.

خلاصة هذه المقالةمخطّط يبيّن أنّ حلّ اسم DLL موضع يسهل أن تظهر فيه مجتمعة أعطال الإقلاع وفروق البيئة ومشاكل الأمن، وأنّه يستحقّ الفهم لا لترتيب البحث فحسب بل لما يعامله Windows أصلاً فرضيّة لحلّ الأسماء.بأيّ ترتيب يبحث (ترتيب المجلّدات)لا يستقيم الشرح إلّا بفهم الاثنينماذا يعامل فرضيّة (القواعد السابقة وفضاء البحث)يمكن إغلاق أعطال الإقلاع وفروق البيئة والأمن معاً

الشكل 24: معرفة العمل في حلّ أسماء DLL ليست حفظ الترتيب، بل فهم الفرضيّة حتى نهايتها.

مقالات ذات صلة

روابط مرجعية

  1. Microsoft Learn: Dynamic-link library search order
  2. Microsoft Learn: Dynamic-Link Library Security
  3. Microsoft Learn: Windows API sets
  4. Microsoft Learn: SetDefaultDllDirectories function
  5. Microsoft Learn: AddDllDirectory function
  6. Microsoft Learn: LoadLibraryEx function
  7. Microsoft Learn: Dynamic-link library redirection
  8. Microsoft Learn: Manifests
  9. Microsoft Learn: About Side-by-Side Assemblies
  10. Microsoft Learn: Process Monitor
  11. Microsoft Learn: ListDLLs
  12. Microsoft Learn: tasklist
  1. Microsoft Learn, Dynamic-link library search order, accessed March 24, 2026 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16 ↩17 ↩18 ↩19 ↩20 ↩21 ↩22 ↩23 ↩24 ↩25

  2. Microsoft Learn, Dynamic-Link Library Security, accessed March 24, 2026 ↩ ↩2 ↩3 ↩4 ↩5

  3. Microsoft Learn, Windows API sets, accessed March 24, 2026 ↩ ↩2 ↩3 ↩4 ↩5 ↩6

  4. Microsoft Learn, SetDefaultDllDirectories function, accessed March 24, 2026 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9

  5. Microsoft Learn, AddDllDirectory function, accessed March 24, 2026 ↩ ↩2 ↩3 ↩4 ↩5 ↩6

  6. Microsoft Learn, LoadLibraryEx function, accessed March 24, 2026 ↩ ↩2 ↩3 ↩4 ↩5 ↩6

  7. Microsoft Learn, Dynamic-link library redirection, accessed March 24, 2026 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7

  8. Microsoft Learn, Manifests, accessed March 24, 2026 ↩ ↩2 ↩3

  9. Microsoft Learn, About Side-by-Side Assemblies, accessed March 24, 2026 ↩ ↩2 ↩3 ↩4

  10. Microsoft Learn, Process Monitor ↩ ↩2

  11. Microsoft Learn, tasklist ↩ ↩2

  12. Microsoft Learn, ListDLLs ↩ ↩2

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

ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.

تطوير تطبيقات ويندوز

لأن موضع ملفات DLL وطريقة تحميلها يتّصلان مباشرة بإمكان إقلاع تطبيق Windows وبشكل التوزيع وبقابلية إعادة الإنتاج عند حدوث مشكلة، فمن المفيد تناولهما في سياق تطوير تطبيقات Windows.

الأسئلة الشائعة

أسئلة شائعة حول موضوع هذه المقالة.

إذا حدّدت اسم DLL في LoadLibrary، بأيّ ترتيب يبحث Windows؟
قبل البحث المتتابع في نظام الملفّات تُقيَّم قواعد سابقة. تحديداً تفعل أوّلاً DLL redirection وAPI set وSxS manifest redirection وloaded-module list وKnown DLLs، ثم يدخل البحث في نظام الملفّات مثل مجلّد التطبيق وSystem32 ومجلّد Windows وcurrent folder وPATH. في الحالة الافتراضيّة مع تفعيل safe DLL search mode يُوضَع current folder في الخلف كثيراً. كما يختلف تفكير ترتيب البحث نفسه بين packaged app وunpackaged app.
إذا حمّلت DLL بمسار كامل، هل تُقرأ DLLات التبعيّة أيضاً من المجلّد نفسه؟
ليس بالضرورة. حتى إن حمّلت أوّل DLL بمسار كامل، تُبحَث DLLات التي يعتمد عليها ذلك الـ DLL باسم الوحدة فقط، فقد تُحَلّ من موضع آخر. إن وُجد هذا الوهم عمل الأمر في بيئة التطوير بينما حُلّت DLL تبعيّة أخرى في جهة التوزيع، فصار العطل معتمداً على البيئة ومزعجاً. إذا أردت التحكّم بما يشمل DLLات التبعيّة فصرّح بفضاء البحث بأعلام LOAD_LIBRARY_SEARCH_* في LoadLibraryEx مثلاً.
لماذا لا ينبغي استخدام SetDllDirectory؟
لأنّه لا يغيّر ترتيب البحث فحسب، بل يملك سلوكاً يعطّل safe DLL search mode فعليّاً. حتى إن أردت إضافة مجلّد واحد خاصّ بالتطبيق، تغيّر فضاء البحث كلّه بما في ذلك التعامل مع current folder، وقد ينعكس أثره أمنيّاً. ثمّ إن استُدعي في العمليّة الأب فقد يصل الأثر إلى ترتيب بحث العمليّة الابن. الأأمن الميل إلى SetDefaultDllDirectories وAddDllDirectory وأعلام بحث LoadLibraryEx.
كيف نمنع DLL hijacking (هجوم DLL preloading)؟
يجمع الهجوم بين التحميل الديناميكي بلا مسار كامل ومجلّد بحث يستطيع المهاجم السيطرة عليه، لذلك نضيّق الاثنين. تحديداً نقلّل LoadLibrary باسم عارٍ، ونستخدم المسار الكامل إن لزم، ونضيّق مسار البحث الافتراضي للعمليّة بـ SetDefaultDllDirectories، ونضيف بـ AddDllDirectory المجلّدات المسموح بها فقط، ونتجنّب current folder والاعتماد غير الحذر على PATH. خصوصاً خطر أن تحمل عمليّة تعمل بامتياز المدير مسار بحث غامضاً، فتُنفَّذ DLL خبيثة بامتياز تلك العمليّة.

الملف الشخصي للمؤلف

صفحة الملف الشخصي لمؤلف المقالة.

غو كومورا

مؤسّس شركة كومورا سوفت ذ.م.م.

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

العودة إلى المدونة