
مُختبر عشوائي موزع، يسترشد بتغطية الكود، ويعتمد على اللقطات (snapshots) لاستهداف وضع المستخدم ووضع النواة على ويندوز ولينكس، مع خلفيات محاكي ومُشغّل افتراضي.
what the fuzzمُختبر اختراق موزَّع، موجه بتغطية الكود، عبر المنصات، قائم على اللقطات، مصمم لاستهداف التطبيقات في وضع المستخدم أو النواة على أنظمة تشغيل مايكروسوفت ويندوز ووضع المستخدم في لينكس (تجريبي!).
ما ذا الفاز أو wtf هو مُختبر اختراق موزَّع، موجه بتغطية الكود، قابل للتخصيص، عبر المنصات، قائم على اللقطات، مصمم لاستهداف التطبيقات في وضع المستخدم أو النواة على مايكروسوفت ويندوز أو لينكس (تجريبي، راجع linux_mode). يمكن تنفيذ الهدف داخل محاكٍ باستخدام bochscpu (الأبطأ، الأكثر دقة)، أو داخل جهاز افتراضي ويندوز باستخدام واجهات برمجة منصة Windows Hypervisor أو داخل جهاز افتراضي لينكس باستخدام واجهات برمجة KVM (الأسرع).
لقد كشف ثغرات في الذاكرة في مجموعة واسعة من البرامج: IDA Pro، لعبة AAA مشهورة، نواة ويندوز، عميل سطح المكتب البعيد من مايكروسوفت، برنامج تشغيل شاشة NVIDIA GPU، إلخ.
الملفات الثنائية المجمّعة متاحة إما من مخرجات CI أو من قسم الإصدارات لكل من ويندوز ولينكس.
إذا كنت ترغب في قراءة المزيد عن تاريخه أو كيفية استخدامه على هدف حقيقي، أنصحك بإلقاء نظرة على هذه المقالات للبدء 🔥
أفضل طريقة لتجربة الميزات هي العمل مع وحدات fuzzer_hevd / fuzzer_tlv_server. يمكنك تحميل الأرشيفات target-hevd.7z / target-tlv_server.7z وفك ضغطها في دليل targets/. تحتوي الأرشيفات على هيكل الدليل المتوقع لكل هدف:
inputs هو المجلد الذي توضع فيه حالات الاختبار المدخلة،outputs هو المجلد الذي تُحفظ فيه ملفات المجموعة الأصغرية الحالية،coverage هو المجلد الذي يُتوقع وجود ملفات .cov فيه،crashes هو المكان الذي تُحفظ فيه حالات التعطل،state هو المكان الذي تُخزَّن فيه صورة الذاكرة (mem.dmp) بالإضافة إلى حالة وحدة المعالجة المركزية (regs.json) ومخزن الرموز (symbol-store.json). مخزن الرموز هو ملف JSON بسيط يُستخدم على أنظمة لينكس لمعرفة أين تضع نقاط التوقف نظرًا لعدم وجود دعم للرموز / dbgeng على تلك الأنظمة. يُنشئ wtf هذا الملف في وقت التشغيل كل مرة تشغّل فيها هدفك على ويندوز.ما يلي يفترض أنك قمت بتحميل ملف target-hevd.7z المرفق بأحدث إصدار، واستخرجته في دليل targets من نسختك من wtf. يجب أن يكون لديك wtf/targets/hevd حيث تجد الأدلة inputs / outputs، إلخ.
الخادم هو أساسًا العقل المسؤول عن تتبع كافة الحالة: تغطية الكود المجمعة، المجموعة، ويُولد ويوزع حالات الاختبار على العملاء.
هذه هي الطريقة التي قد تختار بها تشغيل عقدة خادم محلية:```text wtf.exe master --name hevd --max_len=1028 --runs=10000000
يُستخدم خيار `max_len` لتحديد حجم حالة الاختبار المُنشأة، و`runs` هو عدد حالات الاختبار التي سيتم إنشاؤها، و`address` يُحدد المكان الذي يجب أن يستمع عليه **wtf**، و`target` هو دليل بهيكل الدليل الذي وصفناه أعلاه (يمكن للمستخدم أيضًا اختيار تجاوز تلك الأدلة باستخدام `--input` / `--output` / `--crashes`) و`name` يُحدد اسم وحدة التشويش الخاصة بك بحيث يمكن للمُتحكم الرئيسي استدعاء دالة المُولد الخاصة بك إذا قمت بتعريف واحدة.
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/4a03fc75eed3ed5a92f7f10def697dbf220ae36b0701f05b37759eb432fc0fd9.webp">
</p>
### عُقد التشويش
تقوم عُقد العميل بتشغيل حالة اختبار تم إنشاؤها وتوزيعها بواسطة الخادم وتُعيد النتيجة إلى الخادم (تغطية الكود، النتيجة، إلخ.).
هذه هي الطريقة التي تبدأ بها عقدة عميل تستخدم الواجهة الخلفية *bochscpu*:```text
wtf.exe fuzz --name hevd --limit 10000000
يتم استخدام الأمر الفرعي fuzz مع الخيار name لتحديد وحدة الفازر التي يجب استخدامها، ويحدد backend المحرك التنفيذي و limit الحد الأقصى لعدد التعليمات التي سيتم تنفيذها لكل حالة اختبار (اعتمادًا على المحرك، لهذا الخيار معنى مختلف).
إذا كنت ترغب في تشغيل حالة اختبار (أو مجلد مليء بحالات الاختبار)، يمكنك استخدام الأمر الفرعي run.
هذه هي الطريقة التي يمكنك بها تشغيل حالة الاختبار crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0:```
wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/49b3ca8582d6314724e5615c687472499d5f8f41d04c9543c0a6a070c51f56f8.webp">
</p>
### تقليص مجموعة (مينسيت) لمجموعة بيانات
لتقليص مجموعة بيانات، تحتاج إلى استخدام عقدة خادم وعدد من العقد العميلة حسب الحاجة، كما تفعل في مهمة اختبار الفجوات. يمكنك ببساطة ضبط خيار `runs` على 0.
إليك كيفية تقليص المجموعة في `outputs` إلى دليل `minset` (يظهر أيضًا كيف يمكنك تجاوز الدليلين `inputs` و `outputs`):```
wtf.exe master --name hevd --max_len=1028 --runs=0 --inputs=outputs --outputs=minset
الآلية الأساسية المتاحة للفحص الداخلي في الواجهة الخلفية للتنفيذ هي توليد تتبع التنفيذ. bochscpu هو الواجهة الخلفية الأسرع للقيام بذلك، لأن الخروج من وضع VMX مكلف جدًا في الواجهات الخلفية الأخرى.
هذه هي الطريقة التي يمكنك بها توليد تتبع التنفيذ لحالة الاختبار crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0:```
wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 --trace-type=rip
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/340b4b251e378eaf21c6f5ffdd4f013bd2cef89f23a604cffd0ebf897d27c71f.webp">
</p>
لترميز آثار التنفيذ، يجب استخدام [symbolizer-rs](https://github.com/0vercl0k/symbolizer-rs). هذه هي الطريقة التي ستقوم بها بترميز أثر التنفيذ `crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0.trace` الذي تم إنشاؤه أعلاه:```
symbolizer-rs.exe --trace crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0.rip.trace
إذا وجدت أنك بحاجة إلى المزيد من الوعي السياقي، فإن الخلفية bochscpu تسمح لك بتوليد تتبعات تنفيذية يمكن تحميلها في مستكشف التتبعات Tenet. في المثال أدناه، أبدأ من تعطل في memmove وأتراجع لأعثر على مصدر المؤشر القادم (وضع المستخدم!):```
wtf.exe run --name hevd --limit 10000000 --input crashes\crash-0xfffff764b91c0000-0x0-0xffffbf84fb10e780-0x2-0x0 --trace-type=tenet
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/34df2f5ca8f378db664d3f01bcbefdd43409e300d256d50e3f4f630eb06cc7be.webp">
</p>
### إنشاء تتبعات تغطية الكود
لتوليد تتبعات تغطية الكود، يمكنك ببساطة استخدام الأمر الفرعي `run` مع الخيار `--trace-type=cov`.
هذه هي الطريقة التي ستولد بها تتبعات تغطية الكود لجميع الملفات الموجودة داخل مجلد `minset` وتخزينها في مجلد `coverage-traces`:```
wtf.exe run --name hevd --input minset --trace-path=coverage-traces --trace-type=cov
لا يمكن تحميل هذه التتبعات مباشرة في lighthouse لأنها غير مرمزة.
هذه هي الطريقة التي يمكنك بها ترميز جميع الملفات داخل المجلد coverage-traces وكتابة النتائج في coverage-traces-symbolized:```
symbolizer-rs.exe --trace coverage-traces -o coverage-traces-symbolized --style modoff
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/8a3c3ca18571abef16a9f87f736fe4d52ac102f41095eab4db32533cb5b198b4.webp">
</p>
وأخيرًا، يمكنك تحميل تلك الملفات في [lighthouse](https://github.com/gaasedelen/lighthouse):
<p align='center'>
<img src="https://assets.kitploit.com/production/public/readmes/4699/35d9c25c3d26abf5761fec9e7848ba09d090c7aab32322a740732d82e85964e8.webp">
</p>
أيضًا، إذا كنت لا تهتم بتغطية الكود الفردية، فإن الفرع الرئيسي يحتفظ بملف `coverage.cov` يحتوي على تغطية الكود المجمعة الفريدة التي تم اختبارها. وهذا يسهل التحقق من تغطية الكود العالمية بسرعة أثناء عملية الفازينج.
## كيف يعمل؟
يقوم **wtf** بتشغيل وضع المستخدم ووضع النواة عبر *محطة تنفيذ* ويعتمد على المستخدم في إدخال حالات الاختبار في الهدف. على عكس أدوات الفازينج التقليدية الأخرى، لا يقوم **wtf** بمعظم العمل الثقيل؛ بل يقوم به المستخدم. يحتاج المستخدم إلى معرفة الهدف المُسخَّر جيدًا، ودمج الهدف هو عملية متكررة ستستغرق وقتًا. لكنه يوفر مرونة كبيرة إذا كنت مستعدًا للانخراط في الاختراق :)
سير العمل المعتاد لدمج الهدف هو كالتالي:
1. اجعل هدفك يعمل في جهاز افتراضي من Hyper-V يعمل بنظام Windows مزود بوحدة معالجة مركزية افتراضية واحدة وذاكرة RAM بحجم 4 جيجابايت.
1. ضع هدفك في الحالة المطلوبة باستخدام [KD](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/). على سبيل المثال، لاستهداف معالج IOCTL الخاص بـ [HEVD](https://github.com/hacksysteam/HackSysExtremeVulnerableDriver)، اخترت التوقف في وضع المستخدم قبل أن يستدعي العميل [DeviceIoControl](https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol). سيختلف هذا حسب أهدافك ولكنك تريده على الأرجح أن يكون قريبًا من الكود الذي تريد فازينجه.
```
kd> r
rax=000000dfd98ff3d0 rbx=0000000000000088 rcx=0000000000000088
rdx=00000000deadbeef rsi=0000000000000000 rdi=0000000000000000
rip=00007ff6f5bb111e rsp=000000dfd98ff380 rbp=0000000000000000
r8=000000dfd98ff3d0 r9=0000000000000400 r10=000002263e823055
r11=00007ff6f5bcb54d r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl nz na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
hevd_client!main+0xae:
00007ff6`f5bb111e ff15dc1e0100 call qword ptr [hevd_client!_imp_DeviceIoControl (00007ff6`f5bc3000)] ds:002b:00007ff6`f5bc3000={KERNEL32!DeviceIoControlImplementation (00007ff8`3e2e6360)}
```
1. استخدم [snapshot](https://github.com/0vercl0k/snapshot) لإنشاء تفريغ نواة الأعطال بالإضافة إلى ملف `regs.json` الذي يحتوي على حالة وحدة المعالجة المركزية. أوصي بتفريغ هذه الملفات في مجلد `state` أسفل مجلد `target` الخاص بك (على سبيل المثال `targets/hevd/state`):
```
kd> .load c:\work\codes\snapshot\target\release\snapshot.dll
kd> !snapshot -h
[snapshot] Usage: snapshot [OPTIONS] [STATE_PATH]
Arguments:
[STATE_PATH] The path to save the snapshot to
Options:
-k, --kind <KIND> The kind of snapshot to take [default: full] [possible values: active-kernel, full]
-h, --help Print help
kd> !snapshot c:\work\codes\wtf\targets\hevd\state
[snapshot] Dumping the CPU state into c:\work\codes\wtf\targets\hevd\state\regs.json..
[snapshot] Dumping the memory state into c:\work\codes\wtf\targets\hevd\state\mem.dmp..
Creating c:\\work\\codes\\wtf\\targets\\hevd\\state\\mem.dmp - Full memory range dump
0% written.
5% written. 1 min 50 sec remaining.
10% written. 1 min 17 sec remaining.
15% written. 1 min 30 sec remaining.
[...]
Wrote 4.0 GB in 1 min 32 sec.
The average transfer rate was 44.5 MB/s.
Dump successfully written
[snapshot] Done!
```
1. أنشئ [وحدة فازير](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc)، اكتب الكود الذي [يُدرج حالة اختبار](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L20) في هدفك وحدد [الشروط](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L81) [المختلفة](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L104) [لاكتشاف الأعطال](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L115) أو [نهاية حالة الاختبار](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_hevd.cc#L69).
1. يمكنك أيضًا إنشاء طفرة / مولد خاص بك عن طريق توريث واجهة [Mutator_t](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/mutator.h). يعد [fuzzer_tlv_server.cc](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_tlv_server.cc) مثالًا جيدًا لفهم كيفية تنفيذ خاصتك.
في هذه المرحلة، يجب أن تبدأ في التكرار والتحقق من أن وحدة الفازير تعمل كما هو متوقع. محطات التنفيذ هي صندوق أسود، لذا يجب عليك إنشاء سجلات تنفيذ للتأكد من أنها تسير عبر المسارات الصحيحة وتقوم بالأشياء الصحيحة. خلال هذه المرحلة، أستخدم بشكل أساسي محطة [bochscpu](https://github.com/yrp604/bochscpu) لأنها حتمية تمامًا، وتبدأ بسرعة، ويمكن إنشاء سجلات التنفيذ، وتغطية الكود مجانية، إلخ. بشكل عام، إنها بيئة أفضل للتطوير والنمذجة الأولية.
بمجرد أن تكون راضيًا عن الوحدة، يمكنك البدء في جعلها تعمل مع محطات [winhv](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/whv_backend.h) / [kvm](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/kvm_backend.h) إذا كنت بحاجة إلى تشغيلها تحت تلك المحطات. أحد الاختلافات الرئيسية بين محطة *bochscpu* والمحطات الأخرى هو أن المحطات الأخرى تستخدم نقاط توقف برمجية لتوفير معلومات تغطية الكود. ونتيجة لذلك، ستحتاج إلى تحميل الوحدات التي تريد تغطيتها تحت [IDA](https://hex-rays.com/IDA-pro/) واستخدام برنامج [gen_coveragefile_ida.py](https://github.com/0vercl0k/wtf/blob/HEAD/scripts/gen_coveragefile_ida.py) لإنشاء ملف JSON بسيط يتم تحميله بواسطة wtf. يمكنك إنشاء ملف JSON هذا بنفسك باستخدام أي أداة تريدها: هو في الأساس قائمة بعناوين الكتل الأساسية الافتراضية.
يمكنك أيضًا استهداف تطبيقات [WoW64](https://docs.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details) باستخدام أمر Windbg `!wow64exts.sw` للتبديل إلى سياق 64 بت قبل إنشاء اللقطة (شكرًا لـ [@cube0x8](https://twitter.com/cube0x8) لمشاركة هذه الحيلة!):```
32.kd:x86> !wow64exts.sw
The context is partially valid. Only x86 user-mode context is available.
Switched to Host mode
32.kd> !snapshot
عادةً ما تحمل الأهداف المعقدة حالات معقدة أيضًا، ومن المحتمل أنك قد تحتاج إلى تسليم أكثر من حالة اختبار في جلسة واحدة لإثارة مشكلات معقدة. tlv_server.cc هو مثال على هذا النوع من الخوادم حيث لن يكون تمرين دالة التحليل بحالة اختبار واحدة فقط كافيًا لكشف الأخطاء.
للتعامل مع هذه الحالة، اطّلع على fuzzer_tlv_server.cc الذي يُظهر مثالاً لكيفية حل هذه المشكلة.
يأتي wtf مع اثنين من المغيرين العامين المشهورين: libfuzzer و honggfuzz. قد ترغب في تقديم المغير الخاص بك أو توليد حالات الاختبار بنفسك أيضًا.
للقيام بذلك، يمكنك إنشاء فئة فرعية من واجهة Mutator_t، وتسجيل الوظيفة التي تنشئ المغير الخاص بك عند تعريف وحدة الاختبار التلقائي الخاصة بك:```c++ class CustomMutator_t : public Mutator_t { public: static std::unique_ptr<Mutator_t> Create(std::mt19937_64 &Rng, const size_t TestcaseMaxSize) { return std::make_unique<CustomMutator_t>(Rng, TestcaseMaxSize); } // ... };
Target_t target("target", Init, InsertTestcase, Restore, CustomMutator_t::Create);
اطّلع على فئة [CustomMutator_t](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_tlv_server.cc) في وحدة [fuzzer_tlv_server.cc](https://github.com/0vercl0k/wtf/blob/HEAD/src/wtf/fuzzer_tlv_server.cc) للحصول على مثال كامل.
## واجهات التنفيذ الخلفية
في هذا القسم، أذكر بإيجاز الاختلافات المختلفة بين واجهات التنفيذ الخلفية.
### bochscpu
- ✅ تغطية كاملة لكود النظام (تغطية الحواف متوفرة عبر `--edges`),
- ✅ الترحيل عند الطلب,
- ✅ المهلة هي عدد التعليمات وهو دقيق جدًا,
- ✅ تتبع كامل للتنفيذ مدعوم,
- ✅ حتمي بالكامل,
- ❌ السرعة تبدو جيدة للتنفيذات القصيرة ولكن ليست للتنفيذات الطويلة (~100x أبطأ من KVM عندما كنت أختبر IDA).
### whv
- ✔ تغطية الكود عبر نقاط توقف البرمجيات,
- ❌ الترحيل عند الطلب، لذا فإن بدء التشغيل بطيء (لأنه يحتاج لتحميل تفريغ الأعطال الكامل في الذاكرة),
- ✔ المهلة مطبقة باستخدام مؤقت,
- ✅ تتبع كامل للتنفيذ مدعوم ولكنه بطيء (الخروج من VMX مكلف),
- ✔ حتمي إذا تم التعامل يدويًا مع مصدر عدم الحتمية (على سبيل المثال، تصحيح `nt!ExGenRamdom` الذي يستخدم `rdrand`),
- ✔ السرعة تبدو جيدة للتنفيذات الطويلة (على الرغم من وجود الكثير من الاختناقات في whv؛ ~10x أبطأ من KVM عندما كنت أختبر IDA).
### KVM
- ✔ تغطية الكود عبر نقاط توقف البرمجيات,
- ✅ الترحيل عند الطلب مدعوم عبر UFDD,
- ✔ المهلة مطبقة باستخدام مؤقت. ✅ إذا كان الجهاز يدعم محاكاة PMU، فيتم استخدامه لتوليد [PMI](https://forum.osdev.org/viewtopic.php?f=1&t=27040) بعد X تعليمة منفذة (`MSR_IA32_FIXED_CTR0`),
- ✅ تتبع كامل للتنفيذ مدعوم ولكنه بطيء (الخروج من VMX مكلف),
- ✔ حتمي إذا تم التعامل يدويًا مع مصدر عدم الحتمية (على سبيل المثال، تصحيح `nt!ExGenRamdom` الذي يستخدم `rdrand`),
- ✅ الأسرع للتنفيذات الطويلة (~500م - 1.5 مليار تعليمة؛ ~100x أسرع من *bochscpu*، ~10x أسرع من *whv* عندما كنت أختبر IDA).
## بناء
[CI](https://github.com/0vercl0k/wtf/actions/workflows/wtf.yml) يبني **wtf** على Ubuntu باستخدام كل من [clang++](https://clang.llvm.org/) / [g++](https://gcc.gnu.org/gcc-11/)، وعلى Windows باستخدام [Visual Studio](https://visualstudio.microsoft.com/vs/community/) من Microsoft، وعلى OSX باستخدام [clang++](https://clang.llvm.org/).
لبنائه بنفسك، تحتاج إلى فتح *Visual Studio Developper Command Prompt* وتشغيل إما [build-release.bat](https://github.com/0vercl0k/wtf/blob/HEAD/src/build/build-release.bat) الذي يستخدم مولد [Ninja](https://ninja-build.org/) أو [build-release-msvc.bat](https://github.com/0vercl0k/wtf/blob/HEAD/src/build/build-release-msvc.bat) لإنشاء ملف حل Visual Studio:```
(base) wtf\src\build>build-release.bat
[...]
[2/2] Linking CXX executable wtf.exe
(base) wtf\src\build_msvc>..\build\build-release-msvc.bat
[...]
Finished generating code
wtf.vcxproj -> wtf\src\build_msvc\RelWithDebInfo\wtf.exe
Building Custom Rule wtf/src/CMakeLists.txt
شكر خاص لـ: