C++突破private訪問許可權
阿新 • • 發佈:2019-02-17
假設有以下類:
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中大量運用此手法。