Snapchange - Snapshot-Based Fuzzing مع KVM
Snapchange (من AWS) هو Rust framework لـ snapshot-based fuzzing. Fuzzers التقليدية تعيد تشغيل الهدف لكل إدخال وهذا بطيء بشكل قاتل عندما الأكواد المثيرة للاهتمام تجلس خلف الإعداد المكلف — مصافحة الشبكة أو login أو parsed config أو قاعدة بيانات محملة. Snapchange بدلاً من ذلك يأخذ snapshot الذاكرة من الهدف في بالضبط لحظة الاهتمام ثم يستعيد تلك الحالة ويحور منها باستخدام KVM ويحقق معدلات تكرار عالية جداً ضد الأكواد التي conventional fuzzing تكافح للوصول إليها على الإطلاق.
Snapshot fuzzing يتطلب KVM و root. تشغيل في بيئة معزولة.
المتطلبات
| المتطلب | ملاحظة |
|---|
| Linux مع KVM | /dev/kvm accessible |
| Rust toolchain | Snapchange هي مكتبة Rust |
| QEMU | المستخدمة لأخذ snapshot |
| Root | ل KVM والوصول للذاكرة |
النموذج
| المرحلة | ما يحدث |
|---|
| 1. Snapshot | تشغيل الهدف في QEMU إلى نقطة الاهتمام وتفريغ الذاكرة + السجلات |
| 2. Harness | كتابة fuzzer Rust توصف أين يذهب الإدخال ومتى توقف |
| 3. Fuzz | Snapchange يستعيد snapshot لكل iteration ويحور الإدخال |
| 4. Triage | الأعطال تُسجل مع حالة الذاكرة/السجل بالضبط |
الفكرة الحرجة: جميع تكاليف الإعداد يتم دفعها مرة واحدة في snapshot. كل iteration اللاحقة تبدأ من تلك الحالة في microseconds.
أخذ Snapshot
# مفهومي: تشغيل الهدف تحت QEMU وكسر في الدالة المثيرة للاهتمام
# ثم تفريغ الذاكرة المادية وحالة التسجيل
./snapchange/qemu_snapshot/take_snapshot.sh --target ./my-server
| الأثر | يحتوي على |
|---|
fuzzvm.physmem | كامل الذاكرة المادية الضيف |
fuzzvm.qemuregs | حالة سجل CPU |
*.symbols | جدول الرموز للتغطية/breakpoints |
vmlinux / binary | للترمز |
كتابة Fuzzer
// شكل مفهومي — انظر أمثلة Snapchange للأكواد الكاملة
impl Fuzzer for MyFuzzer {
type Input = Vec<u8>;
const START_ADDRESS: u64 = 0x555555555000;
const MAX_INPUT_LENGTH: usize = 1024;
fn set_input(&mut self, input: &Self::Input, fuzzvm: &mut FuzzVm<Self>) -> Result<()> {
// اكتب الإدخال المحور إلى الذاكرة الضيف حيث يقرأه الهدف
fuzzvm.write_bytes_dirty(VirtAddr(BUFFER_ADDR), CR3, input)?;
Ok(())
}
fn reset_breakpoints(&self) -> Option<&[AddressLookup]> { /* stop conditions */ }
fn crash_breakpoints(&self) -> Option<&[AddressLookup]> { /* crash sites */ }
}
| العنصر | الغرض |
|---|
START_ADDRESS | حيث التنفيذ استئناف كل iteration |
set_input | حقن fuzz input إلى الذاكرة الضيف |
reset_breakpoints | حيث iteration ينتهي عادة |
crash_breakpoints | عناوين تشير إلى crash (على سبيل المثال panic و abort) |
التشغيل
| الأمر | الوصف |
|---|
cargo run -r -- fuzz -c 8 | Fuzz مع 8 cores |
cargo run -r -- project translate | Symbolize/check snapshot |
cargo run -r -- coverage | إنشاء التغطية من corpus |
cargo run -r -- trace <input> | single-step trace input |
cargo run -r -- minimize <input> | انكماش crashing input |
لماذا Snapshot Fuzzing يفوز هنا
| خاصية الهدف | Conventional fuzzing | Snapchange |
|---|
| Expensive startup | يدفعها كل iteration | دفع مرة واحدة |
| Requires auth/handshake | صعب الوصول | Snapshot بعده |
| Stateful protocol | صعب | Snapshot mid-session |
| Kernel/hypervisor code | صعب جداً | Natural fit |
التغطية & Triage
| القدرة | ملاحظة |
|---|
| Breakpoint coverage | التغطية عبر breakpoints على basic blocks |
| Crash dedup | مجموعة بـ faulting address/state |
| Single-step traces | كامل instruction trace لـ input معطى |
| Memory inspection | قراءة ذاكرة الضيف عند crash time |
Snapchange مقابل Fuzzers الأخرى
| الجانب | Snapchange | AFL++ | LibAFL (Nyx) | syzkaller |
|---|
| التنفيذ | KVM snapshot restore | Process fork | Snapshot (Nyx) | VM + syscalls |
| تكلفة الإعداد | دفع مرة واحدة | كل iteration | مرة واحدة | لكل VM |
| الهدف | أي شيء في VM snapshot | Userspace binaries | قابل للتكوين | OS kernels |
| الجهد | عالي (اكتب fuzzer Rust) | منخفض | عالي | متوسط |
قارن مع LibAFL (التي توفر أيضاً snapshot backends) و syzkaller لـ kernel syscall fuzzing.
الموارد