פתרון מבנה PE וטעינה לזיכרון¶
המרת RVA תקינה מתחילה מבדיקת שייכות ל־section, אך אינה מסתיימת בה. צריך לוודא שהטווח המבוקש נמצא בחלק של ה־section שמגובה ב־raw data, ושגם טווח הקובץ המתקבל נמצא בתוך הקובץ.
הפונקציה הבאה משתמשת בחישובי 64 ביט כדי לבדוק שדות PE בני 32 ביט. היא מטפלת ב־section יחיד בלבד; כלי מלא צריך להגדיר מדיניות עבור sections חופפים ולטפל בנפרד בכותרות.
#include <cstdint>
#include <optional>
struct Section {
std::uint32_t rva;
std::uint32_t virtual_size;
std::uint32_t raw_offset;
std::uint32_t raw_size;
};
std::optional<std::uint64_t> file_offset(
const Section& section, std::uint32_t rva,
std::uint32_t length, std::uint64_t file_size) {
const auto begin = static_cast<std::uint64_t>(section.rva);
const auto address = static_cast<std::uint64_t>(rva);
if (address < begin) return std::nullopt;
const auto delta = address - begin;
if (delta > section.raw_size ||
length > static_cast<std::uint64_t>(section.raw_size) - delta)
return std::nullopt;
const auto offset = static_cast<std::uint64_t>(section.raw_offset) + delta;
if (offset > file_size || length > file_size - offset)
return std::nullopt;
return offset;
}
הפונקציה בודקת raw data, ולכן אינה מחזירה bytes עבור אזור וירטואלי שאין לו ייצוג בדיסק. במנתח מחמיר מוסיפים גם הגבלות המתאימות למשמעות הווירטואלית של הטווח, ובודקים consistency של כל כותרות ה־section לפני ביצוע שאילתה. אין להמציא נתוני קובץ עבור אפסים שהטוען מייצר.
המקרה עם e_lfanew גדול נדחה בבדיקת require(nt, 24). החישוב אינו מנסה קודם להוסיף 24 למספר לא אמין. זו בדיוק הסיבה שהבדיקה מנוסחת באמצעות חיסור מהגודל אחרי וידוא שההיסט עצמו נמצא בטווח.
ההפרש בין הבסיסים בדוגמת relocation הוא 0x40000000. הכתובת המתאימה משתנה ל־0x180002000. התיקון חל רק על מיקומים וסוגים שמוגדרים בטבלת ה־relocations. מספר אחר יכול להיות offset, קבוע חשבוני או מידע משתמש, ושינויו יהרוס את התוכנית.
ה־entry point מגיע מתוך כותרת ה־image. בתוכנית C++ רגילה הוא מוביל לקוד startup של ה־runtime, שמכין את סביבת השפה לפני קריאה ל־main. לכן אין סיבה לצפות שה־RVA שלו יהיה ה־RVA של main.
הגשת בדיקה טובה מציגה את הקלט הקטוע ואת השדה הראשון שלא ניתן לקרוא ממנו. אמירה כללית שהכלי לא קרס אינה מספיקה, כי ייתכן שהוא פשוט דילג על הנתיב הפגום.