《一个软件调试的确定与不确定的心得》的心得
抽时间拜读了GG大神的的大作《一个软件调试的确定与不确定的心得》,文中,GG大神得出了这样的推论:推测1.
31位的字符串是从32位中按照某种规律剔除掉了1位。同理,30位的字符串是剔除了2位。
推测2.
31和30位的字符串剔除掉了数字0或者某种规律上的数字0。
为了弄清楚这个问题,我们必须回到MD5算法的具体运算过程上来。
那么,我们还是首先来了解下MD5吧。
Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。MD5的典型应用是对一段信息(Message)产生信息摘要(Message-Digest),以防止被篡改。
MD5功能:
输入任意长度的信息,经过处理,输出为128位的信息(数字指纹);
不同的输入得到的不同的结果(唯一性);
根据128位的输出结果不可能反推出输入的信息(不可逆);
MD5用途:
1、防止被篡改:
1)比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结
果b。如果a与b一样就代表中途未被篡改。2)比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上
公布由安装文件得到的MD5输出结果。3)SVN在检测文件是否在CheckOut后被修改过,也是用到了MD5.
MD5算法过程:
对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列
的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
第一步、填充:如果输入信息的长度(bit)对512求余的结果不等于448,就需要填充使得对512求余的结果等于448。填充的
方法是填充一个1和n个0。填充完后,信息的长度就为N*512+448(bit);
第二步、记录信息长度:用64位来存储填充前信息长度。这64位加在第一步结果的后面,这样信息长度就变为
N*512+448+64=(N+1)*512位。
第三步、装入标准的幻数(四个整数):标准的幻数(物理顺序)是(A=(01234567)16,B=(89ABCDEF)16,C=(FEDCBA98)
16,D=(76543210)16)。如果在程序中定义应该是(A=0X67452301L,B=0XEFCDAB89L,C=0X98BADCFEL,D=0X10325476L)。有点
晕哈,其实想一想就明白了。
第四步、四轮循环运算:循环的次数是分组的个数(N+1)
1)将每一512字节细分成16个小组,每个小组64位(8个字节)
2)先认识四个线性函数(&是与,|是或,~是非,^是异或)
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))
3)设Mj表示消息的第j个子分组(从0到15),<<<s表示循环左移s位,则四种操作为:
FF(a,b,c,d,Mj,s,ti)表示a=b+((a+F(b,c,d)+Mj+ti)<<<s)
GG(a,b,c,d,Mj,s,ti)表示a=b+((a+G(b,c,d)+Mj+ti)<<<s)
HH(a,b,c,d,Mj,s,ti)表示a=b+((a+H(b,c,d)+Mj+ti)<<<s)
II(a,b,c,d,Mj,s,ti)表示a=b+((a+I(b,c,d)+Mj+ti)<<<s)
4)四轮运算
第一轮
a=FF(a,b,c,d,M0,7,0xd76aa478)
b=FF(d,a,b,c,M1,12,0xe8c7b756)
c=FF(c,d,a,b,M2,17,0x242070db)
d=FF(b,c,d,a,M3,22,0xc1bdceee)
a=FF(a,b,c,d,M4,7,0xf57c0faf)
b=FF(d,a,b,c,M5,12,0x4787c62a)
c=FF(c,d,a,b,M6,17,0xa8304613)
d=FF(b,c,d,a,M7,22,0xfd469501)
a=FF(a,b,c,d,M8,7,0x698098d8)
b=FF(d,a,b,c,M9,12,0x8b44f7af)
c=FF(c,d,a,b,M10,17,0xffff5bb1)
d=FF(b,c,d,a,M11,22,0x895cd7be)
a=FF(a,b,c,d,M12,7,0x6b901122)
b=FF(d,a,b,c,M13,12,0xfd987193)
c=FF(c,d,a,b,M14,17,0xa679438e)
d=FF(b,c,d,a,M15,22,0x49b40821)
第二轮
a=GG(a,b,c,d,M1,5,0xf61e2562)
b=GG(d,a,b,c,M6,9,0xc040b340)
c=GG(c,d,a,b,M11,14,0x265e5a51)
d=GG(b,c,d,a,M0,20,0xe9b6c7aa)
a=GG(a,b,c,d,M5,5,0xd62f105d)
b=GG(d,a,b,c,M10,9,0x02441453)
c=GG(c,d,a,b,M15,14,0xd8a1e681)
d=GG(b,c,d,a,M4,20,0xe7d3fbc8)
a=GG(a,b,c,d,M9,5,0x21e1cde6)
b=GG(d,a,b,c,M14,9,0xc33707d6)
c=GG(c,d,a,b,M3,14,0xf4d50d87)
d=GG(b,c,d,a,M8,20,0x455a14ed)
a=GG(a,b,c,d,M13,5,0xa9e3e905)
b=GG(d,a,b,c,M2,9,0xfcefa3f8)
c=GG(c,d,a,b,M7,14,0x676f02d9)
d=GG(b,c,d,a,M12,20,0x8d2a4c8a)
第三轮
a=HH(a,b,c,d,M5,4,0xfffa3942)
b=HH(d,a,b,c,M8,11,0x8771f681)
c=HH(c,d,a,b,M11,16,0x6d9d6122)
d=HH(b,c,d,a,M14,23,0xfde5380c)
a=HH(a,b,c,d,M1,4,0xa4beea44)
b=HH(d,a,b,c,M4,11,0x4bdecfa9)
c=HH(c,d,a,b,M7,16,0xf6bb4b60)
d=HH(b,c,d,a,M10,23,0xbebfbc70)
a=HH(a,b,c,d,M13,4,0x289b7ec6)
b=HH(d,a,b,c,M0,11,0xeaa127fa)
c=HH(c,d,a,b,M3,16,0xd4ef3085)
d=HH(b,c,d,a,M6,23,0x04881d05)
a=HH(a,b,c,d,M9,4,0xd9d4d039)
b=HH(d,a,b,c,M12,11,0xe6db99e5)
c=HH(c,d,a,b,M15,16,0x1fa27cf8)
d=HH(b,c,d,a,M2,23,0xc4ac5665)
第四轮
a=II(a,b,c,d,M0,6,0xf4292244)
b=II(d,a,b,c,M7,10,0x432aff97)
c=II(c,d,a,b,M14,15,0xab9423a7)
d=II(b,c,d,a,M5,21,0xfc93a039)
a=II(a,b,c,d,M12,6,0x655b59c3)
b=II(d,a,b,c,M3,10,0x8f0ccc92)
c=II(c,d,a,b,M10,15,0xffeff47d)
d=II(b,c,d,a,M1,21,0x85845dd1)
a=II(a,b,c,d,M8,6,0x6fa87e4f)
b=II(d,a,b,c,M15,10,0xfe2ce6e0)
c=II(c,d,a,b,M6,15,0xa3014314)
d=II(b,c,d,a,M13,21,0x4e0811a1)
a=II(a,b,c,d,M4,6,0xf7537e82)
b=II(d,a,b,c,M11,10,0xbd3af235)
c=II(c,d,a,b,M2,15,0x2ad7d2bb)
d=II(b,c,d,a,M9,21,0xeb86d391)
5)每轮循环后,将A,B,C,D分别加上a,b,c,d,然后进入下一循环。
看完上面的,晕吧,我们简单点整理如下:
MD5算法:
第一步:增加填充
增加padding使得数据长度(bit为单位)模512为448。如果数据长度正好是模512为448,增加512个填充bit,也就是说填充的个
数为1-512。第一个bit为1,其余全部为0。
第二步:补足长度
将数据长度转换为64bit的数值,如果长度超过64bit所能表示的数据长度的范围,值保留最后64bit,增加到前面填充的数据后
面,使得最后的数据为512bit的整数倍。也就是32bit的16倍的整数倍。在RFC1321中,32bit称为一个word。
第三步:初始化变量:
用到4个变量,分别为A、B、C、D,均为32bit长。初始化为:
A: 01 23 45 67
B: 89 ab cd ef
C: fe dc ba 98
D: 76 54 32 10
第四步:数据处理:
首先定义4个辅助函数:
F(X,Y,Z) = XY v not(X) Z
G(X,Y,Z) = XZ v Y not(Z)
H(X,Y,Z) = X xor Y xor Z
I(X,Y,Z) = Y xor (X v not(Z))
其中:XY表示按位与,X v Y表示按位或,not(X)表示按位取反。xor表示按位异或。
函数中的X、Y、Z均为32bit。
定义一个需要用到的数组:T(i),i取值1-64,T(i)等于abs(sin(i))的4294967296倍的整数部分,i为弧度。
假设前三步处理后的数据长度为32*16*Nbit
第五步:输出:
最后得到的ABCD为输出结果,共128bit。A为低位,D为高位。
还复杂了,再简单点?可以!
MD5算法:
一、补位
二、补数据长度
三、初始化MD5参数
四、处理位操作函数
五、主要变换过程
六、输出结果
好了,码字工作和科普工作结束了,回到我们的主题上来。有兴趣的朋友可以根据整个算法流程在od里跟踪下,也花不了多少时间,这里篇幅有限,只能说重点。GG在文中提到注册码也就是假码在OD里经过一系列的运算,得到一个长的字符串,该字符串长度有时候是32位,有时候是31位,甚至更少,问题的关键就出在了上面说的MD5算法的第六步上,也就是输出结果上。
我手头上MD5源码(delphi版本)的输出函数是通过如下方式来实现:
function MD5Print(D: MD5Digest): string;
var
I: byte;
const
Digits: array of char =
('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');
begin
Result := '';
for I := 0 to 15 do Result := Result + Digits[(D shr 4) and $0f] + Digits and $0f];
end; 通过OD里的跟踪,发现该作者的实现并非采用这个方式:
00408A50/ 08C9 or cl,cl
00408A52|.75 17 jnz short AtomicAl.00408A6B
00408A54|.09C0 or eax,eax
00408A56|.79 0E jns short AtomicAl.00408A66
00408A58|.F7D8 neg eax
00408A5A|.E8 07000000 call AtomicAl.00408A66
00408A5F|.B0 2D mov al,0x2D
00408A61|.41 inc ecx
00408A62|.4E dec esi
00408A63|.8806 mov byte ptr ds:,al
00408A65|.C3 retn
00408A66|[ DISCUZ_CODE_34 ]nbsp; B9 0A000000 mov ecx,0xA
00408A6B|>52 push edx
00408A6C|.56 push esi
00408A6D|>31D2 /xor edx,edx
00408A6F|.F7F1 |div ecx
00408A71|.4E |dec esi
00408A72|.80C2 30 |add dl,0x30
00408A75|.80FA 3A |cmp dl,0x3A
00408A78|.72 03 |jb short AtomicAl.00408A7D
00408A7A|.80C2 07 |add dl,0x7
00408A7D|>8816 |mov byte ptr ds:,dl
00408A7F|.09C0 |or eax,eax
00408A81|.^ 75 EA \jnz short AtomicAl.00408A6D
00408A83|.59 pop ecx
00408A84|.5A pop edx
00408A85|.29F1 sub ecx,esi
00408A87|.29CA sub edx,ecx
00408A89|.76 10 jbe short AtomicAl.00408A9B
00408A8B|.01D1 add ecx,edx
00408A8D|.B0 30 mov al,0x30
00408A8F|.29D6 sub esi,edx
00408A91|.EB 03 jmp short AtomicAl.00408A96
00408A93|>880432 /mov byte ptr ds:,al
00408A96|>4A dec edx
00408A97|.^ 75 FA \jnz short AtomicAl.00408A93
00408A99|.8806 mov byte ptr ds:,al
00408A9B\>C3 retn
为了更好的理解这段函数的功能,我以注册码12345-12345-12345-12345为例,用PYG密码工具算出来标准的MD5结果是:
8620AD0858B57265404EB2727A871ECC,而在OD里的结果却是8620AD858B57265404EB2727A871ECC,是31位的,仔细观察,可以发现中间少了一个0,也就是在输出的过程中,0x86输出为字符串"86",0x20输出为字符串“20”,0xAD输出为字符串“AD”,0x08输出为字符串“8”,而标准的MD5算法中,0x08输出为字符串“08”,所以作者在这里并没有对首位为0的数字在输出的过程中进行处理,而采取了丢弃的动作。这也就导致结果有的是32位,有的是31位。
说的具体点,0x86输出为字符串"86"的过程是 0x86 mod 0xA 得到余数0x6 商是0x8 ,0x6判断下是否为0-9,如果是则0x6+0x30 得到 0x36 ,也就是字符“6”ascii的值,接下来把商0x8 mod 0xA 得到余数0x8,判断是否为0-9,如果是则0x8+0x30,得到0x38,也就是字符“8”ascii的值。最后把“8”和“6”连接起来就是输出结果“86”0x08输出为字符串“08”的过程是0x08 mod 0xA得到余数0x8 商是0x0,0x8判断下是否为0-9,如果是则0x8+0x30 得到 0x38 ,也就是字符“8”ascii的值,但是这里要注意,作者用一个00408A7F|.09C0 |or eax,eax命令来判断商是否为0,如果为0就不继续了,所以接下来的0就忽略了!最后就只有一个字符“8”而不是“08”。这也就是GG大神所说的“剔除”了奇数位上0了。
综上所述,GG大神在最后的推测是正确的。
最后,我们再找个例子来验证下我们的推测。比如表中的第某组:9AC9D23576E1AC62FD1CCA5ED1B28615。
观察了下,没有数字0存在,而且长度正好是32位,说明99.9999%的可能性是MD5;
再看这一组有0存在的:FA9B48813EDF8A6DE3C12A90B9F7F0FF,有数字0存在,长度也等于32位。而且,数字0只在偶数位上!
那么这也间接说明了我的推测是正确的。
这些内置字符串应该是MD5后的结果、且MD5结果中(32位)的奇数位置上的数字0被剔除掉了。
好强悍~~~{:soso_e179:}对MD5的具体实现过程作了分析。猛啊~
我没有去跟踪MD5的具体过程,只是根据附表猜测了一下。感谢兄弟的解答!{:soso_e181:} 沙发开始本来是我的,但是权限是255,抢不到 {:soso_e101:} 楼主对MD5加密分析如此透彻,关键是对GG大神的热情很是羡慕哈@GGLHY 只能看热闹,太复杂了,模拜!!! wgz001 发表于 2015-4-28 16:00
沙发开始本来是我的,但是权限是255,抢不到
权限255 那是为了GG大哥 能坐上沙发而准备的。
很羡慕你们这群算法大婶{:soso_e109:} 终有一天我也会。o(∩_∩)o
Dxer 发表于 2015-4-29 13:04
权限255 那是为了GG大哥 能坐上沙发而准备的。
很羡慕你们这群算法大婶 终有一天我也会 ...
是的,我看好你 {:soso_e182:}
虽然没有看懂,但是仍然给楼主一个大大的赞
页:
[1]