在分析第三方代码时,我遇到了一个hack,该黑客假定基类中的变量偶尔存储在该类的另一个变量中,该变量是从中衍生而来的。派生的类添加了一个校验和一个函数,该函数计算了与派生类中校验和校验和校验和的数据的哈希。
这个hack有效,但似乎不能依靠它做出的假设。以下列出了说明这一点的删节代码(请参阅评论:"丑陋hack!"(:
template<unsigned int BYTES>
class BaseClass {
protected:
unsigned char xdata[BYTES];
public:
BaseClass() { memset(&xdata[0], 0, sizeof(xdata)); };
void FooFn() {};
unsigned char* BigFn() {};
};
template<unsigned int BYTES>
class DerivedClass : public BaseClass<BYTES> {
protected:
__int64 checksum;
__int64 CalcChecksum(unsigned char* pBegin, unsigned char* pEnd);
public:
DerivedClass();
void HashOfDataAndChecksum(unsigned __int64* outputhash);
};
//-----------------------------------------------------------------------
template<unsigned int BYTES>
__int64 DerivedClass<BYTES>::CalcChecksum(unsigned char* pBegin, unsigned char* pEnd) {
__int64 sum = 0;
while (pBegin++ < pEnd)
sum += *pBegin;
return ~sum;
}
template<unsigned int BYTES>
DerivedClass<BYTES>::DerivedClass() {
checksum = CalcChecksum(&BaseClass<BYTES>::xdata[0], &BaseClass<BYTES>::xdata[sizeof(BaseClass<BYTES>::xdata)]);
}
template<unsigned int BYTES>
void DerivedClass<BYTES>::HashOfDataAndChecksum(unsigned __int64* outputhash) {
int i;
unsigned char* pBegin = &BaseClass<BYTES>::xdata[0];
unsigned char* pEnd = (unsigned char*)(&checksum) + sizeof(checksum); //UGLY HACK! Works only if the checksum immediately follows xdata[] in memory.
unsigned char* x = pBegin;
while (x < pBegin + ( (pEnd-pBegin) & 0xFFFFFFFFFFFFFFC0) )
{
for (i = 0; i < 29; i++)
{
XCOMPRESS(x[0], x[8], x[16], x[24], x[32], x[40], x[48], x[56]);
XCOMPRESS(x[24], x[16], x[8], x[0], x[56], x[48], x[40], x[32]);
XCOMPRESS(x[32], x[48], x[0], x[16], x[24], x[40], x[56], x[8]);
XCOMPRESS(x[8], x[32], x[56], x[48], x[40], x[0], x[24], x[16]);
XCOMPRESS(x[48], x[40], x[56], x[0], x[32], x[24], x[16], x[8]);
XCOMPRESS(x[16], x[40], x[8], x[48], x[24], x[56], x[0], x[16]);
XCOMPRESS(x[56], x[48], x[24], x[16], x[8], x[0], x[32], x[40]);
XCOMPRESS(x[8], x[24], x[40], x[56], x[0], x[16], x[32], x[48]);
CROSS_NORMALIZE(&x[0]);
}
x += 64;
}
for (i=0; i < ((pEnd-pBegin) & (size_t)0x3f); i++ )
{
//A lot more hashing code
}
};
int main()
{
BaseClass<1048576> b;
assert(sizeof(b) == 1048576); //This must be true because the code that uses the BaseClass relies on sizeof() this way a lot.
DerivedClass<1048576> d;
assert( sizeof(d) == 1048576 + sizeof(__int64) ); //This must be true because the code that uses the DerivedClass relies on sizeof() to be the sum of the BaseClass and DerivedClass storages.
unsigned __int64 h[8];
d.FooFn(); //This function operates only on the xdata of the BaseClass
d.HashOfDataAndChecksum(&h[0]); //This function calculates a hash of the xdata in the BaseClass concatenated with the checksum in the DerivedClass
}
如何在不诉诸于复制XDATA并将校验和校验和3个变量复制的情况下合法化?...并且不修改哈希算法或基类的大小?
我将使用offsetof
,并断言checksum
的偏移位于预期的位置。您的代码可能会在任何地方工作,但是如果有人弄乱了基类,则您的代码可能不再起作用。最安全的是困难的方法。
使此合法的最简单方法是将基类更改为 xdata[BYTES + sizeof(std::int64_t)]
。这样,您就无需将xdata
和checksum
同时复制到第三个变量。相反,您只需将checksum
复制到xdata
末尾的保留空间。