星空娱乐

原理MD5特点唯一性,是有损加密,不可逆向,现有破解都是用撞库实现不管多长的字符串,加密后长度都是一样长唯一性:一个文件,不管多大,小到几k,大到几G,你只要改变里面某个字符,那么都会导致MD5值改变.作用:很多软件和应用在网站提供下载资源,其中包含了对文件的MD5码,用户下载后只需要用工具测一下下载好的文件,通过对比就知道该文件是否有过更改变动.不可逆性MD5消息摘要算法,属Hash算法一类。MD5算法对输入任意长度的消息进行运行,产生一个128位的消息摘要。MD5的用处不是用来加密信息解密信息的,个人观点:用来做一个全局唯一标记,比如impdx或者图片文件产生的md5永远只会是一个值,我们不用去对比文件或者文本是否相同,只需要判断md5是否相同就可以判断了。算法原理1、数据填充对消息进行数据填充,使消息的长度对512取模得448,设消息长度为X,即满足Xmod512=448。根据此公式得出需要填充的数据长度。填充方法:在消息后面进行填充,填充第一位为1,其余为0。2、添加消息长度在第一步结果之后再填充上原消息的长度,可用来进行的存储长度为64位。如果消息长度大于264,则只使用其低64位的值,即(消息长度对264取模)。在此步骤进行完毕后,最终消息长度就是512的整数倍。3、数据处理准备需要用到的数据:4个常数:A=0x67452301,B=0x0EFCDAB89,C=0x98BADCFE,D=0x10325476;4个函数:F(X,Y,Z)=(X&Y)|((~X)&Z);G(X,Y,Z)=(X&Z)|(Y&(~Z));H(X,Y,Z)=X^Y^Z;I(X,Y,Z)=Y^(X|(~Z));把消息分以512位为一分组进行处理,每一个分组进行4轮变换,以上面所说4个常数为起始变量进行计算,重新输出4个变量,以这4个变量再进行下一分组的运算,如果已经是最后一个分组,则这4个变量为最后的结果,即MD5值。|~运算(6&2)6的二进制:01102的二进制:00106000···0000110(一共32位)&(相同位置都为1,才能去1,否则为0)2000···0000010(一共32位)结果000···0000010结果首位(32位的第一位)为0,是正数二进制,不需要转换,这结果为2。与:1&1=1,1&0=0,0&1=0,0&0=0;//不同时为0,都是1时得1,都是2时得2或:1|1=1,1|0=1,0|1=1,0|0=0;//有1得1,都是0得0;非:~0=1,~1=0;//取反异或运算(^):0^0=0;0^1=1;1^0=1;1^1=0;//不同时为1具体计算的实现较为复杂,建议查阅相关书籍。我们可以这么理解X:字符串长度要求Xmod512=448如果不行则加长度,进行填充,填充第一位为1,其余为0。接着长度如果超过2的64次方位,只取低64位,即对2的64取模接着分组进行循环运算,最后换位就变成了加密MD5MD5的用处用来检验文件是否被修改,通常和sha1或者sha256配合检查,比如txt文件中修改了一个字母,那么他的md5会完全不相同。对于某些明文密码传输,需要保护,普通加密方式具有可逆性,但是MD5不可逆。但常常不会单独使用MD5进行,因为通常的密码都可以通过撞库来获取(撞库:通过用空间换时间的方式,由于MD5的唯一性,我可以用计算机跑出任何字符串的MD5,比如12345的MD5,可以跑出来,也可以通过别人分享来获取)实战利用PHP弱类型来比较MD5例子只演示MD5在PHP中的漏洞什么是弱类型,众所周知PHP是一门弱语言,不必向PHP声明该变量的数据类型,PHP会根据变量的值,自动把变量的值转换为正确的数据类型,但在这个转换过程中就有可能引发一些安全问题。当一个字符串被当作一个数值来取值,其结果和类型如下:如果该字符串没有包含’.',‘e’,'E’并且其数值值在整形的范围之内,该字符串被当作int来取值。其他所有情况下都被作为float来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0。知识点PHP在处理哈希字符串时,会利用"!=“或”=="来对哈希值进行比较,它把每一个以"0E"开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以"0E"开头的,那么PHP将会认为他们相同,都是0。比如s1885207154a这串字符串通过MD5加密后为0e509367213418206700842008763514攻击者可以利用这一漏洞,通过输入一个经过哈希后以"0E"开头的字符串,即会被PHP解释为0,如果数据库中存在这种哈希值以"0E"开头的密码的话,他就可以以这个用户的身份登录进去,尽管并没有真正的密码。输入a=s1885207154a成功绕过最后附上c++版本的MD5实现#include"MD5.h"/*4组计算函数*/inlineunsignedintF(unsignedintX,unsignedintY,unsignedintZ){return(X&Y)|((~X)&Z);}inlineunsignedintG(unsignedintX,unsignedintY,unsignedintZ){return(X&Z)|(Y&(~Z));}inlineunsignedintH(unsignedintX,unsignedintY,unsignedintZ){returnX^Y^Z;}inlineunsignedintI(unsignedintX,unsignedintY,unsignedintZ){returnY^(X|(~Z));}/*4组计算函数结束*//*32位数循环左移实现函数*/voidROL(unsignedint&s,unsignedshortcx){if(cx>32)cx%=32;s=(s<<cx)|(s>>(32-cx));return;}/*B\L互转,接收UINT类型*/voidltob(unsignedint&i){unsignedinttmp=i;//保存副本byte*psour=(byte*)&tmp,*pdes=(byte*)&i;pdes+=3;//调整指针,准备左右调转for(shorti=3;i>=0;--i){CopyMemory(pdes-i,psour+i,1);}return;}/*MD5循环计算函数,label=第几轮循环(1<=label<=4),lGroup数组=4个种子副本,M=数据(16组32位数指针)种子数组排列方式:--A--D--C--B--,即lGroup[0]=A;lGroup[1]=D;lGroup[2]=C;lGroup[3]=B;*/voidAccLoop(unsignedshortlabel,unsignedint*lGroup,void*M){unsignedint*i1,*i2,*i3,*i4,TAcc,tmpi=0;//定义:4个指针;T表累加器;局部变量typedefunsignedint(*clac)(unsignedintX,unsignedintY,unsignedintZ);//定义函数类型constunsignedintrolarray[4][4]={{7,12,17,22},{5,9,14,20},{4,11,16,23},{6,10,15,21}};//循环左移-位数表constunsignedshortmN[4][16]={{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},{1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12},{5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2},{0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9}};//数据坐标表constunsignedint*pM=static_cast<unsignedint*>(M);//转换类型为32位的UintTAcc=((label-1)*16)+1;//根据第几轮循环初始化T表累加器clacclacArr[4]={F,G,H,I};//定义并初始化计算函数指针数组/*一轮循环开始(16组->16次)*/for(shorti=0;i<16;++i){/*进行指针自变换*/i1=lGroup+((0+i)%4);i2=lGroup+((3+i)%4);i3=lGroup+((2+i)%4);i4=lGroup+((1+i)%4);/*第一步计算开始:A+F(B,C,D)+M[i]+T[i+1]注:第一步中直接计算T表*/tmpi=(*i1+clacArr[label-1](*i2,*i3,*i4)+pM[(mN[label-1][i])]+(unsignedint)(0x100000000UL*abs(sin((double)(TAcc+i)))));ROL(tmpi,rolarray[label-1][i%4]);//第二步:循环左移*i1=*i2+tmpi;//第三步:相加并赋值到种子}return;}/*接口函数,并执行数据填充*/unsignedint*MD5(constchar*mStr){unsignedintmLen=strlen(mStr);//计算字符串长度if(mLen<0)return0;unsignedintFillSize=448-((mLen*8)%512);//计算需填充的bit数unsignedintFSbyte=FillSize/8;//以字节表示的填充数unsignedintBuffLen=mLen+8+FSbyte;//缓冲区长度或者说填充后的长度unsignedchar*md5Buff=newunsignedchar[BuffLen];//分配缓冲区CopyMemory(md5Buff,mStr,mLen);//复制字符串到缓冲区/*数据填充开始*/md5Buff[mLen]=0x80;//第一个bit填充1ZeroMemory(&md5Buff[mLen+1],FSbyte-1);//其它bit填充0,另一可用函数为FillMemoryunsignedlonglonglenBit=mLen*8ULL;//计算字符串长度,准备填充CopyMemory(&md5Buff[mLen+FSbyte],&lenBit,8);//填充长度/*数据填充结束*//*运算开始*/unsignedintLoopNumber=BuffLen/64;//以16个字为一分组,计算分组数量unsignedintA=0x67452301,B=0x0EFCDAB89,C=0x98BADCFE,D=0x10325476;//初始4个种子,小端类型unsignedint*lGroup=newunsignedint[4]{A,D,C,B};//种子副本数组,并作为返回值返回for(unsignedintBcount=0;Bcount<LoopNumber;++Bcount)//分组大循环开始{/*进入4次计算的小循环*/for(unsignedshortLcount=0;Lcount<4;){AccLoop(++Lcount,lGroup,&md5Buff[Bcount*64]);}/*数据相加作为下一轮的种子或者最终输出*/A=(lGroup[0]+=A);B=(lGroup[3]+=B);C=(lGroup[2]+=C);D=(lGroup[1]+=D);}/*转换内存中的布局后才能正常显示*/ltob(lGroup[0]);ltob(lGroup[1]);ltob(lGroup[2]);ltob(lGroup[3]);delete[]md5Buff;//清除内存并返回returnlGroup;}