למרבה הצער תיעוד MSDN ו- API של Windows הוא ממש מועט כאן, והתקשיתי למצוא שום דבר אחר מלבד התיאור המינימלי ב- MSDN.
מתברר כי המטפלים הווקטוריים המשך הם מתוחזק ברשימה מקושרת הדומה מאוד לזו המשמשת עבור מטפלים וקטוריים חריגים. הם דומים כל כך, עד שאבות הטיפוס של הפונקציה זהים כמעט.
התבונן ב:
PVOID WINAPI AddVectoredExceptionHandler (_In_ ULONG FirstHandler, _In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler); קוד>
בהשוואה ל:
PVOID WINAPI AddVectoredContinueHandler (_In_ ULONG FirstHandler, _In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler);
למרבה המזל, וקטור חריג המטפלים נפוצים ומתועדים יותר. לדוגמה, ל- MSDN יש דף אודות VEHs, המכיל את הפסקה הבאה:
מטפלים בחריגות וקטוריות הם הרחבה לטיפול בחריגים מובנים. יישום יכול לרשום פונקציה לצפייה או טיפול בכל החריגים ליישום. מטפלים וקטוריים אינם מבוססים על מסגרת, לכן ניתן להוסיף מטפל שייקרא ללא קשר למיקום בו אתה נמצא במסגרת שיחה. מטפלים וקטוריים נקראים לפי סדר הוספתם, לאחר שהבאגים מקבל הודעה על הזדמנות ראשונה, אך לפני שהמערכת מתחילה להתיר את הערימה.
באותו דף יש רק התייחסות לקונית ל ה- API של הוספה והסרה של VCH.
לאחר מחקר והנדסה הפוכה של ntdll, הבנתי ש- VCH ו- VEH דומים למדי ביישום. לדוגמה, ראה כיצד AddVectoredExceptionHandler
ו- AddVectoredContinueHandler
זהים למעט VectoredListIndex
, וציין שיש להוסיף אותם ל VectorHandlerList השני קוד> במקרה של VCH:
באופן דומה, RemoveVectoredExceptionHandler
ו- RemoveVectoredContinueHandler
זהים למעט אינדקס רשימת המטפלים הווקטוריים.
בתוך RtlpAddVectoredHandler
VectoredListIndex
משמש אינדקס ב- _LdrpVectorHandlerList
, שהוא מערך בגודל שני של מבנה רשימה מקושר.
בתמונה הבאה נוכל לראות כיצד מכפילים VectoredListIndex
בגודל של אובייקט העוגן ברשימה, ואז מוסיפים ל _LdrpVectorHandlerList
, שהוא קיזוז הבסיס של המערך.
ועכשיו נגיע לחלק המעניין - איפה VEH ו- VCH שונים?
אם נלך במעלה ההפניות הצולבות ל _LdrpVectorHandlerList
, נבחין ששני הזרימות המובילות לפונקציות הוספה / הסרה כמעט זהות. מלבד ארבעת ממשקי ה- API הללו, נותרה לנו פונקציה אחת נוספת בלבד, הנקראת RtlpCallVectoredHandlers
שאינה מתועדת.
זה די ברור מהשם, אך RtlpCallVectoredHandlers קוד> חוזר על הווקטור (וקטור נבחר על פי האינדקס) וקורא לכל המטפלים ברצף. ברגע שמטפל וקטור מחזיר את EXCEPTION_CONTINUE_EXECUTION
ההפרדה מופסקת על ידי חזרה מוקדמת מ- RtlpCallVectoredHandlers
והביצוע מתחדש.
הפונקציה היחידה הקוראת ל- RtlpCallVectoredHandlers
הוא RtlDispatchException
, שהיא הפונקציה העיקרית לשליחת מטפלים בחריגים.
ראשית, הוא מבצע את כל מטפלי החריגים, החל מהמטפל הראשון בחריגה וקטורית עד האחרון, ואז הולך דרך כל מטפלי החריגים המובנים שמגוללים אותם דרך הערימה. מטפל החריגים הראשון שיחזיר את EXCEPTION_CONTINUE_EXECUTION
(יהיה זה מסוג VEH או SEH) יפסיק את כל תהליך הביצוע של מטפלי החריגים.
כמו VEHs, כאשר VCH נקראים, הם נקראים בזה אחר זה עד שאחד מהם מחזיר EXCEPTION_CONTINUE_EXECUTION
(בדיוק כמו כאשר VEH נקראים), המסמן את RtlpCallVectoredHandlers
ל הפסקה
לולאת השיחות של Vectored Handlers. זה מעניין מכיוון שמשמעותו התקנת מטפל ממשיך וקטורית תחילה מאפשרת לך להסתיר חריגים מ- VCH הבאים.
מטפלים ממשיכים וקטוריים נקראים בנסיבות הבאות: מטפל חריגים (או VEH או SEH) נקרא והחזיר EXCEPTION_CONTINUE_EXECUTION
אם מסיבה כלשהי אימות SEH נכשל (ראה SafeSEH ומנגנונים קשורים), VCH נקרא גם, אך הביצוע לא ימשיך לאחר מכן. ניתן לראות זאת בזרימות רבות המובילות לשיחה RtlpCallVectoredHandlers
מבלי להגדיר bl
ל- 1
ולהשאיר אותה שווה לאפס לפני העברתו אל al
והחזרת false
. פונקציית השיחות, KiUserExceptionDispatcher
ואז תתקשר ל- ZwRaiseException
אם הערך שהוחזר על ידי KiUserExceptionDispatcher
הוא false
.