บทนำ

ในด้านการพัฒนาซอฟต์แวร์ โดยเฉพาะเมื่อจัดการข้อมูลที่สำคัญ ความปลอดภัยของหน่วยความจำเป็นสิ่งสำคัญมาก นักพัฒนามักพบสถานการณ์ที่พวกเขาจำเป็นต้องปกป้องข้อมูลสำคัญ เช่น รหัสการใช้งานหรือรหัสผ่านจากการมองเห็นที่ไม่ได้รับอนุญาต คำถามที่เกิดขึ้นในบริบทดังกล่าวคือ: เราจะสร้างตัวจัดสรรหน่วยความจำที่ปลอดภัยใน C++ ที่ป้องกันการเพจไปยังดิสก์และเข้าถึงได้ยากผ่านดีบักเกอร์ได้อย่างไร?

ในบล็อกโพสต์นี้ เราจะสำรวจปัญหาที่เกี่ยวข้องกับความปลอดภัยของหน่วยความจำและพิจารณาวิธีการนำไปใช้ในการสร้างตัวจัดสรรใน C++ ที่ตอบโจทย์เหล่านี้

การเข้าใจความท้าทาย

ก่อนที่เราจะดำดิ่งไปสู่การแก้ปัญหา สิ่งสำคัญคือต้องเข้าใจความท้าทายบางประการที่มาพร้อมกับการปกป้องหน่วยความจำ:

  • การป้องกันการเพจไปยังดิสก์: บนระบบที่ใช้หน่วยความจำเสมือน หน่วยความจำสามารถถูกเพจไปยังดิสก์ ทำให้เสี่ยงต่อการเข้าถึงที่ไม่ได้รับอนุญาต เมื่อหน่วยความจำถูกเพจออก จะถูกเก็บในพื้นที่สลับซึ่งอาจเข้าถึงได้โดยกระบวนการอื่นๆ

  • การเข้าถึงผ่านดีบักเกอร์: ดีบักเกอร์สามารถเข้าถึงและจัดการหน่วยความจำ ทำให้เป็นสิ่งสำคัญในการหาแนวทางเพื่อลดความเสี่ยงนี้ โดยเฉพาะสำหรับข้อมูลสำคัญ

  • ข้อจำกัดในระดับ OS: ไม่ว่าวัตถุประสงค์ของเราจะเป็นอย่างไร ระบบปฏิบัติการมีการควบคุมกระบวนการและอาจสามารถอ่านเนื้อหาของหน่วยความจำ

ตัวจัดสรรหน่วยความจำที่ปลอดภัยใน C++

แม้จะมีความท้าทายเหล่านี้ แต่เราสามารถ implement ตัวจัดสรรแบบกำหนดเองใน C++ โดยใช้ฟังก์ชัน API ของ Windows มาแบ่งการแก้ปัญหาเป็นขั้นตอนๆ ดังนี้:

1. การใช้ VirtualAlloc

เครื่องมือหลักสำหรับการจัดสรรหน่วยความจำอย่างปลอดภัยคือ VirtualAlloc ฟังก์ชันนี้ช่วยให้เราจัดสรรหน่วยความจำในลักษณะที่กำหนด:

  • เราสามารถตั้งระดับการป้องกันเพื่อควบคุมวิธีการเข้าถึงหน่วยความจำที่จัดสรร
LPVOID pMem = ::VirtualAlloc(NULL, allocLen, allocType, allocProtect);

ที่นี่ allocType ถูกตั้งค่าเป็น MEM_COMMIT และ allocProtect ถูกตั้งค่าเป็น PAGE_READWRITE ซึ่งเปิดใช้งานการเข้าถึงทั้งการอ่านและการเขียน

2. การล็อคหน่วยความจำ

เพื่อป้องกันไม่ให้ระบบปฏิบัติการเพจหน่วยความจำไปยังดิสก์ เราจะใช้ฟังก์ชัน VirtualLock:

if (pMem != NULL) {
    ::VirtualLock(pMem, allocLen);
}

อย่างไรก็ตาม โปรดทราบว่า VirtualLock มีข้อจำกัดในปริมาณของหน่วยความจำที่คุณสามารถจัดสรรได้ ซึ่งอาจเป็นที่จัดการได้ตามความต้องการของคุณ

3. การปล่อยหน่วยความจำอย่างปลอดภัย

เมื่อปล่อยหน่วยความจำ การทำให้เนื้อหาที่สำคัญเป็นศูนย์ก่อนที่จะปล่อยมันเป็นสิ่งสำคัญ ซึ่งสามารถทำได้โดยใช้ SecureZeroMemory:

::SecureZeroMemory(_pPtr, allocLen);
::VirtualUnlock(_pPtr, allocLen);
::VirtualFree(_pPtr, 0, MEM_RELEASE);

ตัวอย่างที่สมบูรณ์

นี่คือตัวอย่างการ implement ตัวจัดสรรหน่วยความจำที่ปลอดภัยของเรา:

template<class _Ty>
class LockedVirtualMemAllocator : public std::allocator<_Ty> {
public:
    template<class _Other>
    LockedVirtualMemAllocator<_Ty>& operator=(const LockedVirtualMemAllocator<_Other>&) {
        return (*this);
    }

    template<class Other>
    struct rebind {
        typedef LockedVirtualMemAllocator<Other> other;
    };

    pointer allocate(size_type _n) {
        SIZE_T allocLen = (_n * sizeof(_Ty));
        DWORD allocType = MEM_COMMIT;
        DWORD allocProtect = PAGE_READWRITE;
        LPVOID pMem = ::VirtualAlloc(NULL, allocLen, allocType, allocProtect);
        if (pMem != NULL) {
            ::VirtualLock(pMem, allocLen);
        }
        return reinterpret_cast<pointer>(pMem);
    }

    void deallocate(void* _pPtr, size_type _n) {
        if (_pPtr != NULL) {
            SIZE_T allocLen = (_n * sizeof(_Ty));
            ::SecureZeroMemory(_pPtr, allocLen);
            ::VirtualUnlock(_pPtr, allocLen);
            ::VirtualFree(_pPtr, 0, MEM_RELEASE);
        }
    }
};

// ตัวอย่างการใช้งาน
typedef std::basic_string<char, std::char_traits<char>, LockedVirtualMemAllocator<char>> modulestring_t;

สรุป

การสร้างตัวจัดสรรหน่วยความจำที่ปลอดภัยใน C++ เป็นงานที่ซับซ้อนซึ่งเกี่ยวข้องกับการนำทางข้อจำกัดในระดับระบบและความท้าทายด้านความปลอดภัย แม้ว่าจะไม่สามารถบรรลุการปกป้องแบบสมบูรณ์จากการเข้าถึงหน่วยความจำได้ แต่การใช้ฟังก์ชันเช่น VirtualAlloc, VirtualLock และ SecureZeroMemory สามารถเพิ่มความปลอดภัยของข้อมูลสำคัญได้อย่างมาก

สิ่งสำคัญคือการตระหนักว่าไม่มีระบบใดสามารถมั่นคงได้อย่างสมบูรณ์จากเจ้าของอุปกรณ์ ดังนั้น การเข้าใจข้อจำกัดเหล่านี้จะช่วยให้นักพัฒนาสามารถสร้างแอปพลิเคชันที่มีความทนทานและแข็งแกร่งมากขึ้น

สำหรับผู้ที่สนใจในการรับรู้ข้อมูลเชิงลึกเพิ่มเติม แหล่งข้อมูลเช่น Practical Cryptography โดย Neil Ferguson และ Bruce Schneier สามารถให้บริบทที่มีคุณค่าในแนวปฏิบัติทางเข้ารหัสและระเบียบวิธีการจัดการหน่วยความจำที่ปลอดภัย