1. 程式人生 > >C++突破private訪問許可權

C++突破private訪問許可權

假設有以下類:

m_nPrivate
class X
{
private:
    int m_nPrivate;
​
public:
    X()
        : m_nPrivate(1)
    {}
​
    template<typename T>
    void Func(const T &t)
    {}
​
    const int GetValue()
    {
        return m_nPrivate;
    }
};

方法1:使用指標

void *p = &x;        // 獲取類的起始地址,其實也就是m_nPrivate資料成員的地址
int *n = (int *)p; ​ int tmp = 2; *n = tmp; // 改寫其值cout << x.GetValue() << endl; // 輸出為2

這種方法的缺點:計算偏移量是個麻煩的事情,涉及到記憶體對齊、編譯器版本等,可移植性低,而且這種方法只能訪問成員變數,卻不能達到“使用private內建型別”的目的。

方法2:使用巨集

在類X的定義前,加一句:

#define private public

該方法可以欺騙編譯器,讓它把“private”當作“public”。

然而它有兩個違背標準的行為:

1)#define 保留字是非法的

2)違反了唯一定義規則(ODR,One Definition Rule),然而類的底層記憶體佈局沒改變,故可行

方法3:使用指標型別轉換——偷天換日

m_nNotPrivate
// 同X的記憶體佈局,將變數or型別定義改為public
class Y            
{
public:
    int m_nNotPrivate;
};
​
void Func(X* xPtr)
{
    (reinterpret_cast<Y*>(xPtr))->m_nNotPrivate = 2;
}

首先我們將X型別的指標轉換為Y型別的指標,在編譯器看來,我們訪問的是Y型別的public成員m_nNotPrivate,因此編譯通過,然而事實上該指標是X型別的,由於Y跟X的記憶體佈局是完全一樣,因此訪問Y的m_nNotPrivate成員實際上也就是在訪問X的m_nPrivate成員。

類似的方法就是,在Y中增加一個非虛成員函式,該函式用來返回m_nPrivate的地址。

方法4:新增友元宣告

類似以上方法,不過是通過新增友元宣告來訪問的~

方法5:利用模版合法鑽空子

如果X中存在一個成員模版,那麼可以這樣子:

namespace
{
    struct Y{};
}
​
template<>
void X::Func(const Y&) //特化
{
    m_nPrivate = 2;
}
​
void Test()
{
    X x;
    cout << x.GetValue() << endl;
​
    x.Func(Y());
    cout << x.GetValue() << endl;
}

這種方法利用了X具有一個成員模板的事實,通過特化函式模版,來打入敵人內部。程式碼完全符合標準,標準也確保這種行為會按照編碼者的意圖行事。boost和loki中大量運用此手法。