ELFhash的个人理解

来源:互联网 发布:淘宝售后在哪评价呢 编辑:程序博客网 时间:2024/05/01 02:59

先上代码,ELF哈希函数(个人看来超简单的了,相比MD5哈希函数来说)。输入字符串,输出字符串的哈希值。

int ELFhash(char* key)
{
unsigned long h=0;
while(*key)
{
h = (h << 4) + *key++;
//printf("1: h =0x%x\n",h);
unsigned long g = h & 0xF0000000L;
//printf("2: g =0x%x\n",g);
if(g)
{
h ^= g >> 24;
//printf("h^=g>>24  3: h =0x%x\n",h);
}
h &= ~g;
//printf("h&=~g   4: h =0x%x\n",h);
}
printf(" ELFhash(key)= 0x%x\n",h%MOD);
//cout<<endl;
return h & 0x7fffffff;

}

初次拿到这个函数的时候,我几乎把h,g的一举一动都打印出来观察了,还是没看出个所以然来。

根据上网查到内容看,这个g是用来防止溢出的。

然后,我开始尝试弱化这个函数,把g去掉,咱就不管它溢出不溢出了,然后函数就变成了这个样子。

int ELFhash(char* key)
{
unsigned long h=0;
while(*key)
{
h = (h << 4) + *key++;
}
return h & 0x7fffffff;

}

瞬间就简单了不是么。几乎一眼就能看出来错位叠加了。

现在我们假设输入的字符串是“hello”,来模拟一下函数的执行内容。

字符串结尾是'\0',它就while循环的结束条件。

进入while循环后,执行的是

h = (h << 4) + key[0];

h = (h << 4) + key[1];

h = (h << 4) + key[2];

h = (h << 4) + key[3];

h = (h << 4) + key[4];

再直观一点,

h = (h << 4) + 0x68;  //  'h'的ASCII值    十进制104   十六进制68

h = (h << 4) + 0x65;    

h = (h << 4) + 0x6c;

h = (h << 4) + 0x6c;

h = (h << 4) + 0x6f;

再直观一点,

    0x0000006f

    0x000006c0

    0x00006c00

    0x00065000

+  0x00680000

____________

现在知道ELF哈希函数的基本原理了吧,因为是错位叠加,字符串中任何一个字符的改变都会令最后得到的哈希值不一样。

通过对比字符串的哈希值,可以知道字符串是否被篡改,从而达到字符串校验的目的。

好了,现在我们回到开头的地方,溢出是怎么回事?

因为原理是左移,错位叠加,当字符串比较长的时候,左移会导致最前面的,最高位的数据丢失。

int型整数不过是32位而已。不考虑溢出,撑死了只能校验长度为31的字符串。

那ELFhash是怎么解决溢出的问题呢?我们把g有关的代码截取出来,看看它做了什么。

g = h & 0xF0000000L;
h ^= g >> 24;
h &= ~g;

第一步,g = h & 0xF0000000,将h的0~27位置零,28~31位不变,赋给g。

我们来模拟一下即将溢出的情况,31个字符的ASCII值错误叠加之后,此时最高的1位(16进制的最高的1位,对二进制来说就是最高的4位)不为1。

如果此时不做任何处理,下一轮第32个字符进来,左移,最高1位就溢出了。

怎么处理呢?

先把h的最高的1位(16进制的最高一位)取出来,判断它是否为0,如果0,说明左移之后没有溢出风险,否则,有溢出风险。

取一个数的某几位通常做法就是按位与。

第二步,h ^= g >> 24。

结合第一步,说白了,就是取h的最高1位(16进制的最高一位),挪到最低1位(16进制的最低一位),再与h本身异或。

我们知道,左移叠加的目的就是为了让每一个字符都对最终的哈希值产生影响。溢出会让最开始的字符失去影响力,最终哈希值只会被倒数共31个字符影响。

那么处理溢出最简单的方式,就是在溢出之前,把即将溢出的高位的影响力转移到低位去。这样一来,无论字符串多长,最后的哈希值会被所有字符影响。

第三步,h &= ~g。

g的最高1位跟h的最高一位相等,g的其余位为0。

结果就是h最高的1位(16进制)全部清0,其余位不变。第二步已经对最高8位的溢出做了处理,把影响力转移了,所以此时清零也无所谓了。


所以,整理下来,ELF哈希做了什么?

一个字符串进去,把每一个字符的ASCII码值错误叠加,最后得到一个整数。可以理解为得到了字符串的“密匙”,但是这个密匙只能甄别字符串是否被篡改,而不能翻译回原来的字符串。

等等......好像忘了点什么    

对,return hash & 0x7fff fff,   最后哈希值还要最高一位(这次是二进制的最高一位)置0。

为什么?因为需要保证出来的哈希值赋过去之后是正整数。如果最高1位(2进制的)是1,而被赋值的数据是有符号的,就会变成负数。


好了全部讲完了,说真的,涉及到位运算的,解释起来都很费劲。位运算很高级,但是不好理解。

真的,如果谁丢了过来一段位运算的代码,还不带任何注释,我会想打人的~~