
ورقة بيضاء
الخلاصة: يُظهر إثبات المفهوم هذا سلسلة متتالية من المعالجات الاستثنائية المنظمة (SEH) مكونة من 3 مراحل ناتجة عن تنفيذ غير محاذٍ متعمد إلى كتل بايت خام التي أول تعليمة صحيحة فيها هي تعليمة
div regبقسمة على صفر.
كل مرحلة استثناء تقوم بتثبيت معالج SEH التالي، وتؤدي إلى قسمة جديدة غير محاذية على صفر، وتنفذ حمولة مرئية غير ضارة قبل استعادة سلسلة SEH الأصلية والإنهاء النظيف.
MOEW (شلال الأكواد غير المحاذية) هو عينة بحث دفاعي تُظهر تنفيذًا متعدد المراحل مدفوعًا بالاستثناءات بشكل متحكم به على x86/Wow64 Windows. يُظهر:
fs:[0]blob1، blob2، blob3)KiUserExceptionDispatcherRtlDispatchExceptionجميع الحمولات غير ضارة:
%TEMP%تم تعطيل إثبات المفهوم عمدًا. لا يتم تشفير أو تعديل أو إتلاف أي بيانات.
div غير محاذٍ في إزاحات متحكم بها (ECX/EDX/EBX = 0)ORIGINAL_SEH)يستخدم إثبات المفهوم ميزات الليلي فقط:
#![feature(asm_experimental_arch)]
#![feature(naked_functions)]
قم بتثبيت المكونات الضرورية:
rustup toolchain install nightly
rustup target add i686-pc-windows-msvc --toolchain nightly
نظرًا لأن إثبات المفهوم يقوم بتثبيت معالجات SEH مخصصة غير موجودة في جدول SAFESEH، يجب توجيه الرابط لتعطيل التحقق من SAFESEH.
أنشئ .cargo/config.toml:
[target.i686-pc-windows-msvc]
rustflags = [
"-C", "link-arg=/SAFESEH:NO",
]
قم ببناء الثنائي:
cargo +nightly build --target i686-pc-windows-msvc --release
المخرج موجود في:
target\i686-pc-windows-msvc\release\seh_waterfall.exe
نفّذ:
seh_waterfall.exe
تدفق التحكم المتوقع:
المرحلة 0 ← blob1 غير محاذٍ ← معالج المرحلة 1
المرحلة 1 ← blob2 غير محاذٍ ← معالج المرحلة 2
المرحلة 2 ← blob3 غير محاذٍ ← المعالج النهائي
النهائي ← استعادة SEH ← خروج
الآثار المرئية:
%TEMP%\moew_stage2.txt (المرحلة 2)على Windows 32 بت، تشكل سجلات SEH قائمة مرتبطة مخزنة في fs:[0]:
#[repr(C)]
struct SehRec {
next: *mut SehRec,
handler: usize,
}
يستخدم كل معالج توقيع SEH القياسي:
extern "system" fn handler(
record: *mut u8,
frame: *mut u8,
context: *mut u8,
dispatcher: *mut u8,
) -> i32
static STAGE_COUNTER: AtomicU32 = AtomicU32::new(0);
static mut ORIGINAL_SEH: *mut SehRec = std::ptr::null_mut();
تستخدم من أجل:
fs:[0] كـ ORIGINAL_SEH.fs:[0] بهذا السجل الجديد.blob1 + 5، الذي يفك تشفيره كـ div ecx (بعد تعيين ECX = 0).notepad.exe.blob2 + 3 → div edx (مع EDX = 0).%TEMP%\moew_stage2.txt.blob3 + 3 → div ebx (مع EBX = 0).calc.exe.ORIGINAL_SEH إلى fs:[0].process::exit(0).blob1#[unsafe(naked)]
pub extern "C" fn blob1() {
naked_asm! {
".byte 0xB8, 0x10, 0x00, 0x00, 0x00", // mov eax, 0x10
".byte 0xF7, 0xF1", // div ecx
".byte 0xC3", // ret
".byte 0x90, 0x90, 0x90", // nop padding
}
}
mov eax, 0x10; div ecx; ret+5: div ecx (مع ECX = 0 → #DE)blob2#[unsafe(naked)]
pub extern "C" fn blob2() {
naked_asm! {
".byte 0x55", // push ebp
".byte 0x8B, 0xEC", // mov ebp, esp
".byte 0xF7, 0xF2", // div edx
".byte 0xC3", // ret
".byte 0x90, 0x90, 0x90", // nop padding
}
}
push ebp; mov ebp, esp; div edx; ret+3: div edx (مع EDX = 0 → #DE)blob3#[unsafe(naked)]
pub extern "C" fn blob3() {
naked_asm! {
".byte 0x53", // push ebx
".byte 0x8B, 0xD8", // mov ebx, eax
".byte 0xF7, 0xF3", // div ebx
".byte 0xC3", // ret
".byte 0x90, 0x90, 0x90", // nop padding
}
}
push ebx; mov ebx, eax; div ebx; ret+3: div ebx (مع EBX = 0 → #DE)تعيد كل مرحلة متعثرة دخول خط أنابيب استثناء وضع المستخدم في Windows:
KiUserExceptionDispatcher
→ RtlDispatchException
→ اجتياز سلسلة SEH (fs:[0])
→ معالج MOEW
عرض المصحح النموذجي:
seh_waterfall!blobX+offset
ntdll!KiUserExceptionDispatcher
ntdll!RtlDispatchException
seh_waterfall!stageN_handler
الشلال مدفوع بالكامل بأعطال الأجهزة الحقيقية وإرسال SEH؛ لا تُستخدم استثناءات اصطناعية أو مزيفة.
يبدو الملف المكتوب في المرحلة 2 كما يلي:
MOEW Stage 2 Marker
-------------------
تم كتابة هذا الملف بواسطة معالج SEH للمرحلة 2
كحمولة توضيحية غير ضارة.
وجودها في %TEMP% يُعد دليلاً بسيطًا ومرئيًا على أن المرحلة 2 تم تنفيذها عبر سلسلة SEH.
تشمل الامتدادات المحتملة:
يتوفر تنفيذ إثبات المفهوم الكامل في هذا المستودع (راجع src/main.rs).
هذا المشروع مخصص للبحث الدفاعي والتعليم. حقوق النشر <2025>
يُمنح الإذن مجانًا لأي شخص يحصل على نسخة من هذا البرنامج والملفات المرتبطة به (البرنامج)، للتعامل مع البرنامج دون قيود، بما في ذلك على سبيل المثال لا الحصر حقوق الاستخدام والنسخ والتعديل والدمج والنشر والتوزيع والترخيص و/أو بيع نسخ من البرنامج، والسماح للأشخاص الذين يتم تزويدهم بالبرنامج بذلك، وفقًا للشروط التالية:
يجب تضمين إشعار حقوق النشر أعلاه وإذن الإذن هذا في جميع النسخ أو الأجزاء الجوهرية من البرنامج.
يتم توفير البرنامج "كما هو"، دون أي ضمان من أي نوع، صريحًا أو ضمنيًا، بما في ذلك على سبيل المثال لا الحصر ضمانات القابلية للتسويق والملاءمة لغرض معين وعدم الانتهاك. في أي حال من الأحوال لا يكون المؤلفون أو مالكو حقوق النشر مسؤولين عن أي مطالبة أو أضرار أو مسؤولية أخرى، سواء في دعوى تعاقدية أو تقصيرية أو غير ذلك، تنشأ عن أو فيما يتعلق بالبرنامج أو استخدامه أو التعاملات الأخرى في البرنامج.
العينة الحقيقية من MOEW التي ألهمت إثبات المفهوم هذا لم تستعيد fs:[0] في نهاية التنفيذ.
بدلاً من ذلك، كانت مرحلتها النهائية:
fs:[0]) إما بـ NULL أو بمؤشر إلى ذاكرة غير صالحة.RtlDispatchException لمؤشر معالج غير صالح.unknown.unknown.Application Error من Event Viewer بإزاحات عطل لا معنى لها.KiUserExceptionDispatcher،يخدم خطوة فساد SEH المدمرة هذه الغرض الأساسي للعينة المضاد للتحقيق الرقمي:
محو السلسلة السببية وإنتاج تعطل نهائي غير منسوب.
على عكس العينة الحقيقية، يقوم إثبات المفهوم هذا بما يلي:
يلتقط رأس SEH الأصلي أثناء المرحلة 0:
asm!("mov {old}, fs:[0]", old = out(reg) old_head);
ORIGINAL_SEH = old_head;
يستعيد رأس SEH الأصلي في المعالج النهائي:
asm!("mov fs:[0], {p}", p = in(reg) ORIGINAL_SEH);
ينتهي نظيفًا عبر process::exit(0) بدلاً من إطلاق عطل غير معالج.
نتيجة لذلك، إثبات المفهوم:
Application Error 1000 في Event Viewer،ومع ذلك—بشكل حاسم—إثبات المفهوم لا يلغي سلوك تدهور القياس عن بعد المدفوع بالاستثناء الخاص بـ MOEW.
يقوم إثبات المفهوم بالتأكيد بتدهور القياس عن بعد بنفس الطريقة الأساسية:
يتعمد إطلاق أعطال أجهزة متعددة غير محاذية.
ينتج استثناءات متعددة من الفرصة الأولى في تتابع سريع.
يجبر Windows على تنفيذ متكرر:
KiUserExceptionDispatcher
RtlDispatchException
→ معالج مخصص
→ كتلة غير محاذية
→ عطل في الأجهزة
يولد أكوام استدعاء غير خطية ومسيطرة على الاستثناءات.
يشوه إعادة بناء تدفق التحكم في المصححات و EDR عن طريق توجيه التنفيذ عبر:
وبالتالي، يعيد إثبات المفهوم إنتاجًا أمينًا:
...مع تجنب التعطل النهائي المدمر.
هذا يجعل إثبات المفهوم مثاليًا للقياس والبحث دون استدعاء الحمولة المضادة للتحقيق الرقمي الكاملة.
نظرًا لأن إثبات المفهوم يحافظ على السلوك العام لـ MOEW باستثناء تعطل فساد SEH، فهو:
يقوم إثبات المفهوم بنمذجة شلال الاستثناء (جوهر MOEW) مع إزالة التوقيع المدمر النهائي. يُظهر أن تدهور القياس عن بعد ينشأ ليس فقط من فساد SEH، ولكن من نموذج تدفق التحكم المدفوع بالاستثناء نفسه.
هذا القسم يُضفي طابعًا رسميًا على الاختلافات السلوكية مع الحفاظ على وضوح الغرض من إثبات المفهوم:
إظهار شلال MOEW بشكل آمن ومناسب للبحث دون التعطل النهائي المضاد للتحقيق الرقمي.
| السلوك | عينة MOEW الحقيقية | تنفيذ إثبات المفهوم |
|---|
| شلال الأكواد غير المحاذية | ✔ | ✔ |
| آلة الحالة المدفوعة بـ SEH المتكرر | ✔ | ✔ |
| أكوام استدعاء إرسال الاستثناءات المهيمنة | ✔ | ✔ |
| تدهور القياس عن بعد / تتبع الأكوام | ✔ | ✔ |
| فساد SEH المتعمد | ✔ | ❌ |
| مؤشرات SEH معلقة أو غير صالحة | ✔ | ❌ |
| استثناء نهائي غير معالج | ✔ | ❌ |
| تعطل WER "وحدة غير معروفة" | ✔ | ❌ |
استعادة نظيفة لـ fs:[0] | ❌ | ✔ |
| إنهاء نظيف | ❌ | ✔ |