轉載於 http://blog.csdn.net/wangjiee…
如果侵權,麻煩聯絡刪除
我們在寫程式碼的時候,按約定都是把成員資料放到private訪問區中,然後在通過相應的函式來存取。那又有什麼樣的程式碼可以突破訪問許可權來直接操作類中private區段中的成員資料呢?
首先,我們想到了指標,對吧~指標可是萬能之王,然而也是萬惡之源。那我們就先來看看指標如何突破馬其諾防線的。
先定義一個測試類
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;
}
};
很簡單是吧,私有成員m_nPrivate就是我們的目標。
利用指標偏移
來看看突破程式碼:
void *p = &x;// 獲取類的起始地址,其實也就是m_nPrivate資料成員的地址
int *n = (int *)p;
int tmp = 2;
*n = tmp; // 改寫其值
cout << x.GetValue() << endl; // 輸出為2
偽造者方式:
類定義覆蓋(改寫類定義)
這個伎倆是現將某個有待偽造的類定義複製一份,然後通過該複製後的“贗品”來達到目的,且看
void Hijack(X &x)
{
x.m_nPrivate = 2;
}
class X
{
// 手工新增
friend ::Hijack(X &);
// 這裡是複製X類定義
private:
int m_nPrivate;
public:
X()
: m_nPrivate(1)
{}
template<typename T>
void Func(const T&t)
{}
const int GetValue()
{
return m_nPrivate;
}
};
這個伎倆被
VC2008
的編譯器逮住了,沒有編譯通過。因為他違反了唯一定義規則(ODR,One Definition Rule)。看來語言律師還是不會放過這種沒腦子的造假者!打假、打假,越打越假!
偷竊者方式:
用
#define private public
偷樑換柱
偷偷的改變定義類的含義。且看:
#define private public// 萬惡的巨集伎倆啊
void Hijack(X &x)
{
x.m_nPrivate = 2;
}
他的兩根手指頭很靈活喲。在VC2008
成功執行得到。然而他卻有兩個違背標準的行為:
1)#define
保留字是非法的
2)違反了唯一定義規則(ODR),然而類的底層記憶體佈局沒改變,故可行
騙子方式:
仿造相同記憶體結構的新public類,指標型別轉換
// 同X的記憶體佈局,只有一個int型的變數
class BaitSwitch
{
public:
int m_nNotPrivate;
};
void Func(X &x)
{
(reinterpret_cast(x)).m_nNotPrivate=2;
}
在VC2008上成功執行達到目的,但是卻有漏洞:
標準中reinterpret_cast的行為未定義,VC2008允許返回的結果引用。所以也成功讓騙子得逞。
語言律師方式
律師就是鑽法律的漏洞,永遠也不會被逮住,他是在鑽法律的空子!且看
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
中大量運用此手法。
模板編譯的過程在實際編譯以前…活生生的給X類
新增了一個成員函式.
原來,類裡面有一個實現了的模板函式!
那麼在外面再加一個模板函式,導致模板推演的過程中多出來一個自己寫的並且加入了備選組中,所以相當於多了一個過載。
並且由於此函式的引數是匿名空間裡面的特定的類別,完全避免了攪亂原本該函式的功能。
高招啊!
看法:
我相信這並不是C++訪問控制機制的漏洞,或許,我們本不應該這樣做。用成員模板提供一種有效的訪問似有成員資料可以繞過類的訪問控制,這也許就是我們想達到的目的