Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
NullGate — مكتبة تسهّل استخدام استدعاءات النظام غير المباشرة (indirect syscalls). تقنية مثيرة للاهتمام لتجاوز أنظمة مكافحة الفيروسات وEDR كإثبات للمفهوم (PoC). | Kitploit
أدوات/GitHubGitHub/0xsch1zo/nullgate
أدوات التشفير/فك التشفيرشيل كودما بعد الاستغلالالفريق الأحمرتطوير الحمولاتالهجوم العدائي
GitHub0xsch1zo/nullgate

NullGate

مكتبة تسهّل استخدام استدعاءات النظام غير المباشرة (indirect syscalls). تقنية مثيرة للاهتمام لتجاوز أنظمة مكافحة الفيروسات وEDR كإثبات للمفهوم (PoC).

عرض المستودع
16819منذ سنة واحدةتمت المراجعة من قبل Kitploit

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

NullGate

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

العرض التوضيحي

توضيح للعينة

البناء

لبناء العينة استخدم -DNULLGATE_BUILD_SAMPLE=ON. إذا بنيت nullgate مباشرةً، سيكون الملف التنفيذي في <build_dir>/sample.exe، وإذا بنيته كاعتمادية (dependency) فسيكون في <build_dir>/_deps/nullgate-build/sample.exe. على ويندوز، نظرًا لأن مسارات البناء غريبة، فغالبًا سيكون في نفس الدلائل الأساسية لمواقع العينات لكن متداخلًا أكثر. يستقبل البرنامج معرف العملية (PID) التي تريد حقن الشيل كود فيها كوسيط.

[!WARNING] إذا كنت تستخدم لينكس، يجب تثبيت المترجم المختلط mingw. على Arch مثلًا يمكنك تنفيذ pacman -S mingw-w64-gcc. ثم استخدم الخيار -DNULLGATE_CROSSCOMPILE=ON لتعيين mingw كمترجم افتراضي.

[!TIP] يُنصح أيضًا بإزالة الرموز من الملف الثنائي الناتج (strip) لتقليل احتمالية الاكتشاف.

root@kitploit:~
git clone https://github.com/0xsch1zo/NullGate
cd NullGate
cmake . -B build -DNULLGATE_BUILD_SAMPLE=ON
cmake --build build/

hashser المهجور (الإصدارات 1.1.3 وما دون)

يمكن بناؤه باستخدام العلم -DNULLGATE_DEPRECATED_HASHER.

الاستخدام

إضافة nullgate إلى مشروعك

يُدعم CMake FetchContent. إليك مثال على ملف CMakeLists.txt بسيط:

root@kitploit:~
cmake_minimum_required(VERSION 3.25)

include(FetchContent)

FetchContent_Declare(nullgate
    GIT_REPOSITORY https://github.com/0xsch1zo/NullGate
    GIT_TAG 1.2.0 
)

FetchContent_MakeAvailable(nullgate)

project(test)

add_executable(test
    main.cpp
)

target_link_libraries(test
    PRIVATE nullgate
)

الربط يتم بشكل ثابت (statically) لذلك لا داعي للقلق بشأن ظهور الرموز.

[!NOTE] الأمثلة التالية ستستخدم namespace ng = nullgate

استدعاءات النظام

الاستخدام مباشر جدًا، إليك مقتطف يوضح الوظيفة الرئيسية:

root@kitploit:~
ng::syscalls syscalls;
typedef NTSTATUS NTAPI NtAllocateVirtualMemory(
    _In_ HANDLE ProcessHandle,
    _Inout_ _At_(*BaseAddress,
                 _Readable_bytes_(*RegionSize) _Writable_bytes_(*RegionSize)
                     _Post_readable_byte_size_(*RegionSize)) PVOID *BaseAddress,
    _In_ ULONG_PTR ZeroBits, _Inout_ PSIZE_T RegionSize,
    _In_ ULONG AllocationType, _In_ ULONG PageProtection);

NTSTATUS status = syscalls.SCall<NtAllocateVirtualMemory>(
      ng::obfuscation::fnv1Const("NtAllocateVirtualMemory"), processHandle,
      &buf, 0, &regionSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

هناك أمان أنواع مدمج. كل ما عليك هو توفير تعريف دالة nt التي تريد استدعاءها! يمكنك الحصول عليه بسهولة من ntdoc. هذه هي الطريقة الموصى بها لاستخدام المكتبة.

الواجهة السابقة غير المهجورة

لمن لا يحبون السحر الأسود لقالب C++ أو ما شابه، الواجهة السابقة ما تزال متاحة:

root@kitploit:~
NTSTATUS status = syscalls.Call(ng::obfuscation::fnv1Const("NtAllocateVirtualMemory"),
                         processHandle, (PVOID)&buf, (ULONG_PTR)0, &regionSize,
                         (ULONG)(MEM_RESERVE | MEM_COMMIT), (ULONG)PAGE_EXECUTE_READWRITE);

باستخدام هذه الواجهة تحتاج إلى تحويل (cast) الوسائط إلى النوع الصحيح، وعدم القيام بذلك قد يسبب مشاكل.

التشفير / التجزئة

تجزئة استدعاءات ntapi

root@kitploit:~
constexpr uint64_t hash = ng::obfuscation::fnv1Const("NtAllocateVirtualMemory");

طريقة fnv1Const الموضحة سابقًا تجلب متعة لغة C++ الحديثة إلى عالم maldev. وهي دالة consteval، لذا من المضمون أن تُقيَّم في وقت الترجمة، لتحل محل اسم الدالة القابل للقراءة بتجزئة fnv1.

يوجد أيضًا مكافئ وقت تشغيل يُدعى fnv1Runtime لكنه بالطبع لا يضيف فائدة إخفاء أسماء دوالنا. يستخدمه التنفيذ الداخلي للتحقق من أي دالة داخل ntdll يحصل منها على رقم استدعاء النظام.

تشفير xor العام

هناك ثلاث روتينات لتشفير xor:

xorConst
root@kitploit:~
constexpr ng::obfuscation::ConstData xored = ng::obfuscation::xorConst("some string");
std::cout << xored.string();

مخرج محتمل:

root@kitploit:~
<Y:-E&X0

هذه الروتين، على غرار دالة fnv1Const، تُقيَّم في وقت الترجمة، لذلك لن يظهر "some string" أبدًا في الملف الثنائي، لأن السلسلة ستُخزَّن بصيغتها المشفرة. لكن مهلاً، نحتاج فعلًا لاستخدام تلك البيانات! ConstData هو غلاف رفيع حول البايتات الخام بحجم ثابت. لديه طريقتان: raw() و string(). raw() تُرجع std::vector<unsigned char> و string كما يوحي الاسم تُرجع std::string.

[!NOTE] إذا كنت بحاجة إلى إنشاء ConstData مباشرةً من سلسلة حرفية فاستخدم std::to_array لإنشاء مصفوفة وسيطة تُمرَّر إلى ConstData. لاحظ مع ذلك أن هذا الأسلوب سيجعل ConstData يخزّن حرف النهاية الفارغ الإضافي للسلسلة الحرفية.

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

xorRuntime

xorRuntime هو الروتين الثاني المتاح. كما يوحي الاسم، فهو مكافئ وقت التشغيل لـ xorConst. لاحظ أنه لا يُستخدم لفك التشفير فقط، بل يعمل في الاتجاهين، رغم أن استخدامه للتشفير لا يوفر فوائد إخفاء السلسلة في وقت الترجمة.

root@kitploit:~
ng::obfuscation::ConstData xored = ng::obfuscation::xorConst("some string");
ng::obfuscation::ConstData data = ng::obfuscation::xorRuntime(xored);
assert(data.string() == "some string");

std::string dynamicString = "some data of dynamic size";
auto dynamicData = ng::obfuscation::DynamicData(dynamicString);
auto xoredDynamic = ng::obfuscation::xorRuntime(dynamicData);
auto unxoredDynamic = ng::obfuscation::xorRuntime(xoredDynamic);
assert(unxoredDynamic.string() == dynamicString);

DynamicData لديه نفس طرق ConstData مع بعض المنشئات الإضافية للتوافق، وكما يوحي الاسم يمكن أن يكون بحجم غير معروف في وقت الترجمة. xorRuntime يقبل كلاً من DynamicData و ConstData.

xorRuntimeDecrypted

بينما يمكننا دائمًا فك التشفير في وقت التشغيل باستدعاء xorConst أولاً ثم تمرير النتيجة إلى xorRuntime، إلا أن ذلك ممل نوعًا ما. هنا يأتي حل هذه المشكلة، وهو الجزء الأجمل: xorRuntimeDecrypted، دالة مفيدة للسلسلة الحرفية التي لا تحتاج إلى معالجة وهي في الحالة المشفرة. إنها تشفر السلسلة الحرفية في وقت الترجمة ثم تفك تشفيرها في وقت التشغيل، كل ذلك في استدعاء واحد. أليس رائعًا!

root@kitploit:~
ng::obfuscation::ConstData text = ng::obfuscation::xorRuntimeDecrypted<"some string">();
assert(data.string() == "some string");
المفتاح

قد يقول أحدهم: كل شيء رائع، لكن أين المفتاح؟ في nullgate 1.2 يتم توليد المفتاح عشوائيًا في كل بناء جديد! (أي عند تشغيل أمر cmake) هذا يقلل من فرصة رصد التوقيعات أكثر.

تجاوز فحص الذاكرة في ويندوز ديفندر

جوهر المشكلة هو أنه عندما نستدعي NtCreateRemoteThreadEx أو NtCreateProcess، يتم تشغيل فحص للذاكرة ويتم اكتشاف حمولة msfvenom التي تحمل توقيعات واضحة جدًا.

كيف نتجاوز ذلك؟

أحد الحلول المعروفة هو أنه عند استدعاء NtAllocateVirtualMemory نضبط صلاحيات الصفحة إلى PAGE_NOACCESS أولاً، ثم ننشئ الخيط في حالة معلّقة (suspended). عندها سيفشل ويندوز ديفندر في فحص ذاكرة عمليتنا. يمكننا بعد ذلك استئناف تنفيذ الخيط باستخدام NtResumeThread. هذا يعمل، لكن ماذا لو كان الحل الأمني المستخدم أكثر كفاءة؟ ماذا سيفعل؟ بالطبع سيستخدم VirtualProtect لتغيير صلاحيات صفحتنا واكتشاف msfvenom. لتجاوز ذلك غيّرت الاستراتيجية قليلاً. بدلاً من ضبط الصفحة على PAGE_NOACCESS، أثناء كتابتنا الأولى إلى ذاكرة العملية يمكننا ببساطة وضع بعض البيانات غير المهمة (junk data) في العملية (نعم هذا مطلوب، أو أنا فقط غبي جدًا لأجد طريقة تعمل بدونه). ثم ننشئ خيطًا في حالة معلّقة. بعد ذلك نكتب الشيل كود المطلوب إلى العملية، وأخيرًا نستأنف الخيط باستخدام NtResumeThread. بهذه التقنية لا داعي للقلق من الوصول إلى ذاكرتنا بعد استدعاء NtCreateThreadEx لأنه لا يوجد شيء هناك. فقط بعد ذلك يُكتب الشيل كود المفكوك التشفير ويُستأنف التنفيذ.

شكر وتقدير:

  • إلى @ElephantSe4l و @MarioBartolome على طريقتهما الرائعة في الاسترجاع الديناميكي لأرقام استدعاءات النظام، وعلى المشروع بأكمله الذي استلهمت منه كثيرًا.
  • إلى @cr-0w على مقالة المدونة الرائعة والفيديو اللذين يناقشان استدعاءات النظام المباشرة وغير المباشرة.
  • إلى bordergate على المقالة التي تصف الطريقة الأولية للتجاوز.

إخلاء مسؤولية

صُنعت هذه المكتبة لأغراض أكاديمية فقط. المؤلفون غير مسؤولين عما يُقدَّم لهذه المكتبة، وبالتالي نحن معفيون من أي مسؤولية تنشأ عن سوء استخدامها.

تنزيل الأداة