很早时候就想写这个东东了。在网上下载了一个C语言的MD5代码,后来朋友又发给我一份用 mm0 mm1 等寄存器(64位)的汇编代码,两者都能完成MD5运算。
MD5运算的结果是一个“摘要”,16B数据。输入可以是无穷的,但输出是有限的(16B)。可见它是一个不可逆算法(是有损变换)。
网上有说MD5被破解了,不是很确切。下载了fastcoll程序代码,看了一下,它只是相当快地找到两个不一样的输入,而输出是一样的,这就是说它能快速实现了MD5碰撞。可惜程序并不能自己构造一个输入,使得输出的结果是事先指定好的,也就是说它不能找到一个指定的16B所对应的输入。
所以,要是想到得一个输出所对应的输入,只有查表法和穷举法了。查表法要的是超大硬盘,穷举法要的是速度(可配合字典)。
为了追求速度,会用到汇编程序实现MD5,因为MD5中的 FF GG HH II 变换用到的 & | ~ ^ + 还有循环移位等都很适合汇编指令。而用C程序写的MD5程序编译后,看它的汇编指令中有很多可优化的地方。
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) // C语言实现的循环移位
编译后为
mov eax, dword ptr [ebp-14] // mov eax, x
shl eax, 0C // eax << n
mov ecx, dword ptr [ebp-14] // mov ecx, x
shr ecx, 14 // ecx >> 32- n
or eax, ecx // eax = eax | ecx
mov dword ptr [ebp-14], eax // mov x, eax
上面做了N步,其实就是一个循环移位,本来用一条指令就行了的 rol eax, n
mov eax, dword ptr [ebp-14] // mov eax, x
rol eax, n // 循环左移 n位
mov dword ptr [ebp-14], eax // mov x, eax
void CMD5::MD5Transform( uInt state[4], const uChar block[64] ) // MD5变换函数(是MD5运算消耗时间最多的地方)
{
uInt a, b, c, d, x[16];
memcpy (x, block, 64); // 把变换的数据复制到 x[16]中,再由x[16]去参加运算
... ...
}
看了MD5 F G H I变换 FF GG HH II变换,并没有改变 x[16]的值,所以复制数据是多余的,这一步可省去。
变换中用了uInt a, b, c, d,整个变换过程反反复复地读内存写内存,其实没必要。我用了 eax,ebx,ecx,edx 代替它们了。程序是C中加入汇编写成的。在变换函数中,还使用了 edi, esi, ebp 这样恰好够用。整个程序中没有用到mm0 mm1 等寄存器。
用汇编宏替换了原来的C语言宏:
#define F_asm(x, y, z) #define G_asm(x, y, z) ......
#define FF_asm(a, b, c, d, x, s, ac) #define GG_asm(a, b, c, d, x, s, ac) ......
MD5算法的输入单位是 bit,但在计算机中,我们传给MD5算法的都是 Byte,所以我把程序中相关bit的地方都作了修改,简化程序。
生成的是一个MD5_asm.dll,导出函数如下图:
比较一下MD5主要的变换过程,C版的和 MMX版的 都有1300多条汇编指令,但C版的内存读写(相对寄存器之间操作而言)多过MMX版。
而MD5_asm中的主要变换过程不到600行,并且所含内存读写指令相当少,运算速度可想而知。
MD5_asm.dll 和 .h .lib 部分代码 与测试程序代码 已上传,可于个人上传资源中找到。下载地址:
http://download.youkuaiyun.com/source/2453975