#include
using namespace std;
class Secret {
public:
Secret() : m_int(1865), m_long(9527) {}
private:
int m_int;
long m_long;
};
int main() {
Secret obj;
Secret* p = &obj;
// 編譯錯誤:私有成員不可存取
// cout << obj.m_int << endl;
// 但指標運算照樣可讀寫!
cout << "m_int = " << *(reinterpret_cast(p)) << endl; // 1865
*reinterpret_cast(p) = 8888; // 修改
cout << "m_int = " << *(reinterpret_cast(p)) << endl; // 8888
cout << "m_long = " << *(reinterpret_cast(reinterpret_cast(p) + sizeof(int))) << endl; // 9527
}
「在特定存取區塊內,成員變數在記憶體中連續排列;程式執行前,所有存取指定符都消失了。執行期物件只是一塊儲存區,別無他物。」
—— 《C++ 程式設計哲學》
private/protected 防止程式設計師誤用。reinterpret_cast + 偏移量即可任意讀寫。| 層級 | 手段 | 防禦目標 |
|---|---|---|
| 語言規範 | private, friend, getter/setter | 開發時誤用、介面契約 |
| 記憶體佈局 | Pimpl 慣用法 (不透明指標) | 隱藏實作細節、減少編譯依賴 |
| 進程隔離 | 獨立進程 + IPC (gRPC, COM, 共享記憶體) | 惡意/失誤的同進程存取 |
| 硬體/OS | 記憶體保護金鑰 (MPK), SGX Enclave, SELinux | 同進程內的惡意代碼、側信道攻擊 |
| 加密 | 敏感資料加密儲存,使用時解密 | 記憶體傾印洩露、冷啟動攻擊 |
// Secret.h
class Secret {
struct Impl; std::unique_ptr pImpl;
public:
Secret(); ~Secret();
int getInt() const;
void setInt(int);
};
// Secret.cpp
struct Secret::Impl { int m_int; long m_long; };
Secret::Secret() : pImpl(std::make_unique()) { pImpl->m_int = 1865; }
Secret::~Secret() = default;
int Secret::getInt() const { return pImpl->m_int; }
void Secret::setInt(int v) { pImpl->m_int = v; }
使用者只見介面,Impl 定義在 .cpp,編譯防火牆生效,指標偏移攻擊失效 (不知偏移)。
private 是給人的規矩,不是給機器的牆。真正的安全需分層:編譯期封裝 → 介面隱藏 → 進程隔離 → 硬體加密。理解這一點,才能在正確層級投入適當防護成本。