
إطار عمل لربط واجهات برمجة التطبيقات (API Hooking) لاعتراض ومراقبة تطبيقات ويندوز
ApiScope هي أداة بحث لنظام Windows x64 لتتبّع استدعاءات مختارة لواجهات برمجة التطبيقات (API) في عملية جديدة أو قيد التشغيل. وهي تتبع أحداث تحميل DLL عبر واجهة برمجة تطبيقات مصحح أخطاء Windows (debugger API) وتثبّت خطافات (hooks) قبل أن تستكمل العملية المُصحَحة (debuggee) التنفيذ بعد كل حدث.
الخطافات المضمّنة:
ntdll.dll!NtCreateFile (يسجّل مسار الهدف)ntdll.dll!NtOpenFile (يسجّل مسار الهدف)ntdll.dll!NtReadFilentdll.dll!NtWriteFilentdll.dll!NtClosentdll.dll!NtOpenKey (يسجّل مسار المفتاح)ntdll.dll!NtSetValueKeyntdll.dll!NtQueryValueKeybcrypt.dll!BCryptOpenAlgorithmProviderالمتطلبات:
cmake -S . -B build -A x64
cmake --build build --config Release
ctest --test-dir build -C Release --output-on-failure
powershell -ExecutionPolicy Bypass -File .\scripts\validate-apiscope-hooks.ps1 .\build\bin\Release\apiscope-hooks.dll
powershell -ExecutionPolicy Bypass -File .\scripts\smoke.ps1 -SkipBuild
يجري جلب Zydis v4.1.1 عند إصدار ثابت (pinned commit) ويُربط فقط داخل apiscope.exe. وتبقى apiscope-hooks.dll المحقونة بلا استيرادات (import-free).
apiscope.exe [--help | --version | --list-hooks]
apiscope.exe run -k <module!export|all> [-k <module!export>] [-f text|jsonl] [-o <path>] [-q] -- <program> [args...]
apiscope.exe attach -p <pid> -k <module!export|all> [-k <module!export>] [-f text|jsonl] [-o <path>] [-q]
أسماء الخطافات مؤهلة دائمًا باسم الوحدة (module). تطابق الوحدة غير حساس لحالة الأحرف؛ بينما تطابق التصدير (export) حساس لحالة الأحرف.
.\apiscope.exe run `
--hook ntdll.dll!NtCreateFile `
--hook bcrypt.dll!BCryptOpenAlgorithmProvider `
-- C:\path\app.exe
.\apiscope.exe attach --pid 4242 --hook all
.\apiscope.exe run --hook all --format jsonl --output trace.jsonl --quiet -- app.exe
تبقى أحداث الطرفية (terminal) مقروءة بينما يرسل --output النص أو JSONL اختياريًا إلى ملف. يثبّط --quiet مرآة أحداث الطرفية. يتحكم --color auto|always|never في لون الطرفية (الافتراضي auto: يعمل عند TTY، ويتعطل عند التوجيه عبر أنبوب أو تحت NO_COLOR)؛ ولا يظهر اللون أبدًا في ملفات --output. على TTY، تعرض ApiScope أيضًا تذييل حالة مباشرًا (الأحداث، الإسقاطات، المعدل، وعدّادات كل خطاف) يُعاد رسمه في مكانه؛ تحكم فيه عبر --status auto|always|never.
[*] ntdll.dll!NtWriteFile ----------
timestamp : 2026-06-07T17:44:23.3834340Z
thread_id : 3508
sequence : 3
file_handle : 0x000000000000008C
length : 16
buffer_ascii : Hello, ApiScope!
result : STATUS_SUCCESS (0x00000000)
تحتوي أحداث JSONL على بيانات وصفية عامة وحقول خاصة بالخطاف:
{"schema_version":1,"sequence":1,"module":"bcrypt.dll","api":"BCryptOpenAlgorithmProvider","hook":"bcrypt.dll!BCryptOpenAlgorithmProvider","fields":{"flags":0,"result":"STATUS_SUCCESS (0x00000000)"}}
راجع SCHEMA.md لمعرفة غلاف الحدث وترميزات الحقول حسب النوع (تُعرض المؤشرات والحالات كسلاسل سداسية عشرية 0x).
اضغط Ctrl+C أو Ctrl+Break لاستعادة الخطافات النشطة، وتحرير الأدوات البعيدة (instrumentation)، والفصل. تستمر العملية الهدف في التشغيل. عند الخروج الطبيعي، تطبع ApiScope حالة الهدف بالعشري والست عشري، يليها ملخص الجلسة (الأحداث، الإسقاطات، وعدّادات كل خطاف) على stderr.
تحلّ NtCreateFile وNtOpenFile وNtOpenKey مسار الهدف path من OBJECT_ATTRIBUTES وتبلّغ عن المقبض (handle) الناتج. تسجّل ApiScope كل عملية فتح ناجحة وتعلّق العمليات اللاحقة على نفس المقبض — القراءات، الكتابات، الوصول لقيم السجل، وNtClose المطابق — بالمسار path المُحل، بحيث يصبح النشاط مقروءًا دون تتبّع يدوي للمقابض. كما تُخرج NtClose المقبض من التتبع، وتُحل عمليات الفتح النسبية عبر مقبض root_directory شوهد مسبقًا.
[*] ntdll.dll!NtCreateFile ----------
sequence : 2
path : test_file.txt
file_handle : 0x000000000000008C
result : STATUS_SUCCESS (0x00000000)
[*] ntdll.dll!NtReadFile ----------
sequence : 3
file_handle : 0x000000000000008C
buffer_ascii : Hello, ApiScope!
result : STATUS_SUCCESS (0x00000000)
path : test_file.txt
يُربط path في أحداث القراءة والكتابة من المقبض، ولا يُلاحظ من الاستدعاء نفسه.
كل خطاف هو ملف واحد داخل src/apiscope-hooks/hooks/ ويصرّح معًا عن وحدة المصدر، والتصدير، والمعالج، وفتحة الترامبولين (trampoline slot)، واصطلاح الاستدعاء، والتوقيع:
DEFINE_API_HOOK(
BCryptOpenAlgorithmProvider,
"bcrypt.dll",
"BCryptOpenAlgorithmProvider",
NTSTATUS,
WINAPI,
PVOID* Algorithm,
const wchar_t* AlgorithmId,
const wchar_t* Implementation,
ULONG Flags) {
TraceEvent event;
InitializeTraceEvent(&event, "bcrypt.dll", "BCryptOpenAlgorithmProvider");
AddTraceUInt32(&event, "flags", Flags);
NTSTATUS result = CALL_ORIGINAL(
BCryptOpenAlgorithmProvider,
Algorithm,
AlgorithmId,
Implementation,
Flags);
AddTraceStatus(&event, "result", result);
EmitTraceEvent(&event);
return result;
}
يكتشف apiscope.exe --list-hooks واصفات ثابتة التخطيط يتم تصديرها بواسطة DLL الخطاف. لا يلزم قائمة API مركزية أو مخطط أحداث في جانب المُشغِّل.
sequenceDiagram
actor User
participant ApiScope as apiscope.exe
participant Debugger as Windows debugger API
participant Target as Target process
participant Modules as Loaded DLLs
participant Hooks as apiscope-hooks.dll
participant Ring as Shared-memory ring
User->>ApiScope: run program or attach PID
alt run
ApiScope->>Debugger: CreateProcess(DEBUG_ONLY_THIS_PROCESS)
else attach
ApiScope->>Debugger: DebugActiveProcess(PID)
end
ApiScope->>Debugger: DebugSetProcessKillOnExit(FALSE)
Debugger-->>ApiScope: CREATE_PROCESS_DEBUG_EVENT
ApiScope->>Target: Map import-free hook image
ApiScope->>Ring: Create and initialize bounded ring
ApiScope->>Target: Map shared section with NtMapViewOfSection
loop CREATE_PROCESS / LOAD_DLL events
Debugger-->>ApiScope: Module base and file handle
ApiScope->>Modules: Register module name and base
alt ntdll.dll loaded
ApiScope->>Target: Build unpatched NtReadVirtualMemory bypass
end
ApiScope->>Modules: Resolve module.dll!Export and forwarders
alt export and dependencies are loaded
ApiScope->>Hooks: Resolve handler and trampoline slot
ApiScope->>Target: Allocate trampoline and patch export
else dependency is not loaded yet
ApiScope->>ApiScope: Keep hook pending
end
ApiScope->>Debugger: ContinueDebugEvent
end
Target->>Hooks: Call patched API
Hooks->>Target: CALL_ORIGINAL through trampoline
Hooks->>Ring: Publish bounded TLV event
opt first event in a pending batch
Hooks->>Target: Signal reader through unpatched NtSetEvent
end
Ring-->>ApiScope: Drain event batches
ApiScope-->>User: Readable text and optional JSONL
opt UNLOAD_DLL_DEBUG_EVENT
Debugger-->>ApiScope: Module unloaded
ApiScope->>Target: Release associated hook state
ApiScope->>Debugger: ContinueDebugEvent
end
alt target exits
Debugger-->>ApiScope: EXIT_PROCESS_DEBUG_EVENT and exit status
ApiScope->>Ring: Drain queued events
ApiScope-->>User: Print decimal and hexadecimal exit status
else Ctrl+C or Ctrl+Break
User->>ApiScope: Stop tracing
ApiScope->>Target: DebugBreakProcess
ApiScope->>Target: Restore hooks and free instrumentation
ApiScope->>Debugger: DebugActiveProcessStop
ApiScope-->>User: Target continues running
end
ينشئ المتحكم (controller) قسمًا مدعومًا بملف ترحيل الصفحات (paging file) ويُخططه في العمليتين معًا. تنشر خيوط الخطاف إلى حلقة (ring) متعددة المنتجين ذات سعة ثابتة باستخدام أرقام تسلسلية لكل فتحة. يرسل تجاوز NtSetEvent غير المُرقَّع (unpatched) إيقاظًا واحدًا مدمجًا لكل دفعة معلّقة، ويُفرغ المتحكم الأحداث في دفعات. لا ينتظر المنتجون القارئ أبدًا؛ فإذا امتلأت الحلقة تُسقط الحدث وتعدّه. لا تزال معاينات المخازن المؤقتة تستخدم تجاوز NtReadVirtualMemory غير المُرقَّع، بحيث تفشل مؤشرات الهدف غير الصالحة دون تعطيل الخطاف.
cmake/ Dependency configuration
docs/ Event schema and format reference
scripts/ Validation and runtime smoke tests
src/apiscope/ CLI, debugger, mapper, patcher, and renderer
src/apiscope-hooks/ Import-free hook DLL and standalone hooks
src/include/ Shared contracts
tests/ Unit and runtime test targets
ntdll.dll!NtClose عندما يكون هذا الخطاف نشطًا؛ وبدونه، يحتفظ المقبض المغلق الذي يُعاد استخدامه بمساره السابق.DebugSetProcessKillOnExit(FALSE) الهدف حيًا، لكن الخطافات والأدوات المُخططة تبقى مقيمة حتى يخرج الهدف.استخدم ApiScope فقط على الأنظمة والعمليات التي لديك إذن بفحصها. راجع SECURITY.md وCONTRIBUTING.md وROADMAP.md.
MIT. تحتفظ مكونات الطرف الثالث بتراخيصها؛ راجع THIRD_PARTY_NOTICES.md.