
مشروع بحثي يعمل على الهندسة العكسية لواجهات COM الخاصة بمركز أمان Windows لتتبع تسجيل برامج مكافحة الفيروسات عبر ATL وvtable وWSCAPI وRPC، مع التحقق في وقت التشغيل عبر WMI.
OWN-Defender هو مشروع بحث أمني في Windows يركز على فهم كيفية قيام مركز أمان Windows (WSC) بتمثيل وإدارة منتجات الأمان المضادة للفيروسات من خلال واجهات COM الخاصة به.
بدأ المشروع كتحقيق في السلوك الذي أظهره DefendNot، ولكن بدلاً من التعامل مع التنفيذ الحالي كصندوق أسود، استخدمته كنقطة انطلاق للهندسة العكسية والتحقق المستقلين.
الهدف من هذا المشروع هو فهم مسار التنفيذ الكامل:
COM
↓
CLSID / IID
↓
CoCreateInstance
↓
QueryInterface
↓
ATL Interface Map
↓
vtable
↓
IWscAVStatus4
↓
CWscIsv
↓
WSCAPI.dll
↓
RPC
↓
Windows Security Center
تم تطوير المشروع واختباره في بيئة بحث Windows خاضعة للتحكم.
للاستخدام البحثي/التعليمي فقط
هذا المشروع مخصص لأبحاث داخل Windows، والهندسة العكسية، والتعليم الأمني، واختبارات الأمان المصرح بها. لا تستخدمه للتدخل في برامج الأمان على الأنظمة التي لا تملكها أو ليس لديك إذن صريح لاختبارها.
كان السؤال الأولي بسيطًا:
كيف يعرف مركز أمان Windows أن منتج مكافحة الفيروسات موجود؟
بدلاً من التوقف عند توثيق واجهة برمجة التطبيقات العامة، أردت فهم ما يحدث تحت الواجهة.
أدى هذا إلى عدة أسئلة:
QueryInterface() الواجهة؟__int64 a1 فقط لطريقة ما؟Register() هي فعليًا دالة تسجيل مكافحة الفيروسات؟كانت الخطوة الأولى هي تحديد فئة COM الخاصة بمركز أمان Windows.
يستخدم المشروع فئة COM الخاصة بـ WSC:
CLSID_WscIsv
F2102C37-90C3-450C-B3F6-92BE1693BDF2
يحتوي التنفيذ أيضًا على منطق لتحديد موقع CLSID ديناميكيًا من سجل Windows بدلاً من الاعتماد حصريًا على قيمة ثابتة.
من الناحية المفاهيمية:
HKLM
└── SOFTWARE
└── Classes
└── CLSID
└── {CLSID}
└── Windows Security Center ISV API
قدم هذا أول علاقة مهمة:
Registry
↓
CLSID
↓
Windows Security Center ISV API
كان التحدي التالي هو تحديد واجهة COM التي يجب طلبها.
يستخدم المشروع:
IWscAVStatus4
مع:
4DCBAFAC-29BA-46B1-80FC-B8BDE3C0AE4D
كان أحد الدروس المهمة من البحث:
اسم GUID وحده ليس دليلاً كافيًا.
تحققت من العلاقة من خلال الهندسة العكسية بدلاً من افتراض أن اسم الواجهة وGUID صحيحان.
شمل التحقيق:
QueryInterface_ATL_INTMAP_ENTRYQueryInterfaceكانت إحدى أكثر خطوات الهندسة العكسية فائدة هي تتبع تنفيذ:
CComAggObject<CWscIsv>::QueryInterface()
الذي يصل في النهاية إلى:
ATL::CComObjectRootBase::InternalQueryInterface()
تُستخدم خريطة واجهة ATL لمقارنة IID المطلوب مع إدخالات الواجهة المسجلة.
من الناحية المفاهيمية:
Requested IID
↓
QueryInterface()
↓
InternalQueryInterface()
↓
ATL Interface Map
↓
GUID comparison
↓
Matching interface
↓
Interface pointer
قدم هذا دليلاً مستقلاً على أن GUID قيد التحقيق يتوافق فعليًا مع واجهة COM المتوقعة.
Register()كان أحد أكبر تحديات الهندسة العكسية هو فهم سبب عدم عرض IDA/Ghidra دائمًا لتوقيع الطريقة الذي توقعته.
تحتوي الواجهة المعاد بناؤها على:
virtual HRESULT __stdcall Register(
BSTR path,
BSTR name,
unsigned int,
unsigned int
) = 0;
ومع ذلك، يمكن أن يعرض المفكك تنفيذًا مثل:
_IWscAVStatus4<CWscIsv>::Register(__int64 a1)
في البداية بدا هذا غير متسق.
أظهر التحقيق الإضافي أن تمثيل المفكك كان يصف غلافًا/كعبًا والاستدعاء غير المباشر الأساسي عبر vtable بدلاً من تقديم توقيع الواجهة المنطقي الكامل.
أصبح هذا درسًا مهمًا:
مخرجات المفكك هي تفسير لرمز الآلة، وليست الحقيقة الأصلية على مستوى المصدر.
لحل هذه التناقضات، قارنت:
COM interface definition
↓
vtable layout
↓
assembly
↓
wrapper/thunk
↓
calling convention
↓
actual target function
Register() المتعددةكان مصدر ارتباك آخر هو وجود دوال متعددة بأسماء مثل:
IWscAVStatus2::Register
IWscAVStatus4::Register
IWscFWStatus2::Register
RegisterAV
كان الإدراك المهم أن الأسماء المتشابهة لا تعني واجهات متطابقة.
على سبيل المثال:
AV
↓
IWscAVStatus4
↓
AV registration
بينما:
Firewall
↓
IWscFWStatus2
↓
Firewall registration
كان يجب التحقق من رقم الواجهة والتنفيذ المحيط بها بدلاً من اختيار دالة لمجرد أن اسمها يحتوي على Register.
كان هذا من أكثر الأجزاء فائدة في البحث لأنه أجبرني على الربط بين:
Interface
+
IID
+
vtable
+
implementation
+
parameter layout
+
call target
بعد تحديد الواجهة الصحيحة، تتبعت عملية التسجيل أعمق داخل الملف الثنائي.
كان المسار الملاحظ تقريبًا:
IWscAVStatus4::Register()
↓
CWscIsv
↓
RegisterSecurityProductFunction
↓
wscRegisterSecurityProduct()
↓
WSCAPI.dll
↓
s_wscRegisterSecurityProduct()
↓
NdrClientCall3()
↓
RPC
↓
Windows Security Center
كان هذا مهمًا بشكل خاص لأن طريقة COM نفسها لم تكن العملية النهائية.
عبر الاستدعاء في النهاية حدود RPC.
غيّر ذلك طريقة رؤيتي للبنية:
COM
≠
final implementation
بدلاً من ذلك:
COM
↓
local implementation
↓
WSC API
↓
RPC client
↓
Windows component
WSCAPI.dllكانت الطبقة التالية هي WSCAPI.dll.
وصل المسار المعكوس هندسيًا إلى:
wscRegisterSecurityProduct()
الذي استدعى في النهاية:
s_wscRegisterSecurityProduct()
ثم:
NdrClientCall3()
كانت هذه هي النقطة التي انتقل فيها التحقيق من استدعاء COM عادي إلى بنية RPC في Windows.
ساعد فهم هذه الطبقة في تفسير سبب عدم إمكانية فهم السلوك بالكامل من خلال النظر فقط إلى DLL الأصلي لـ COM.
كان التحليل الثابت جزءًا واحدًا فقط من البحث.
بعد إعادة بناء الواجهة ومسار الاستدعاء ذي الصلة، أنشأت تنفيذي الخاضع للتحكم وقارنت السلوك الناتج مع مركز أمان Windows.
استخدمت معلومات مركز أمان Windows / WMI مثل:
ROOT\SecurityCenter2
AntiVirusProduct
لمراقبة معلومات المنتج المسجل بشكل مستقل.
كانت عملية التحقق:
Reverse engineering
↓
Interface reconstruction
↓
Own implementation
↓
Runtime execution
↓
Windows Security Center
↓
WMI observation
↓
Compare results
سمح لي هذا بالتحقق من أن استنتاجات التحليل الثابت تتوافق مع سلوك Windows الملحوظ.
علّمني هذا المشروع أكثر بكثير من مجرد التفاعل مع واجهة COM واحدة.
CoCreateInstanceQueryInterfaceWSCAPI.dllNdrClientCall3الأهم من ذلك، تعلمت تجنب الاعتماد على دليل واحد فقط.
بدلاً من ذلك:
Symbol
↓
Decompiler
↓
Assembly
↓
GUID
↓
Interface Map
↓
vtable
↓
Call Graph
↓
RPC
↓
Runtime Verification
كل طبقة تزيد الثقة في الاستنتاج.
يتكون تنفيذ البحث من مكونين رئيسيين.