算法识别之Md5

先来谈谈最熟悉的MD5吧,小时候就知道这个加密算法了。

Md5算法简介

算法简介内容来源自网络。

一、MD5概念

MD5,全名Message Digest Algorithm 5 ,中文名为消息摘要算法第五版,为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。上面这段话话引用自百度百科,我的理解MD5是一种信息摘要算法,主要是通过特定的hash散列方法将文本信息转换成简短的信息摘要,压缩+加密+hash算法的结合体,是绝对不可逆的。

二、MD5计算步骤

MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
第一步、填充
如果输入信息的长度(bit)对512求余的结果不等于448,就需要填充使得对512求余的结果等于448。填充的方法是填充一个1和n个0。填充完后,信息的长度就为N*512+448(bit);

第二步、记录信息长度
用64位来存储填充前信息长度。这64位加在第一步结果的后面,这样信息长度就变为N512+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)先认识四个线性函数(&是与,|是或,~是非,^是异或)

1
2
3
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))

3)设Mj表示消息的第j个子分组(从0到15),<<< s表示循环左移s位,则四种操作为:

1
2
3
4
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)四轮运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
第一轮
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的实现原理,直接来个例子分析一下,例子有很多,就直接拿加密与解密这本书中的例子来说吧。

例子很简单直接GetWindowTextA断点,回溯找到验证函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
0040114A |. 68 C9000000 push 0xC9 ; /Count = C9 (201.)
0040114F |. A4 movs byte ptr es:[edi],byte ptr ds:[esi] ; |
00401150 |. 8BB424 E00300>mov esi,dword ptr ss:[esp+0x3E0] ; |
00401157 |. 894424 21 mov dword ptr ss:[esp+0x21],eax ; |
0040115B |. 51 push ecx ; |Buffer = NULL
0040115C |. 66:894424 29 mov word ptr ss:[esp+0x29],ax ; |
00401161 |. 68 E8030000 push 0x3E8 ; |ControlID = 3E8 (1000.)
00401166 |. 56 push esi ; |hWnd = 00090812 ('MD5 *KeyGenMe*',class='#32770')
00401167 |. 885C24 20 mov byte ptr ss:[esp+0x20],bl ; |
0040116B |. 884424 33 mov byte ptr ss:[esp+0x33],al ; |
0040116F |. FFD5 call ebp ; \GetDlgItemTextA
00401171 |. 8BF8 mov edi,eax
00401173 |. 3BFB cmp edi,ebx
00401175 |. 0F84 0E010000 je MD5KeyGe.00401289
0040117B |. 8D5424 60 lea edx,dword ptr ss:[esp+0x60]
0040117F |. 68 C9000000 push 0xC9 ; /Count = C9 (201.)
00401184 |. 52 push edx ; |Buffer = 0019F674
00401185 |. 68 E9030000 push 0x3E9 ; |ControlID = 3E9 (1001.)
0040118A |. 56 push esi ; |hWnd = 00090812 ('MD5 *KeyGenMe*',class='#32770')
0040118B |. FFD5 call ebp ; \GetDlgItemTextA
0040118D |. 83F8 13 cmp eax,0x13 ; 判断序列号长度是否为0x13
00401190 |. 0F85 F3000000 jnz MD5KeyGe.00401289
00401196 |. 8A4C24 64 mov cl,byte ptr ss:[esp+0x64]
0040119A |. B0 2D mov al,0x2D
0040119C |. 3AC8 cmp cl,al ; 判断第五位是否为-
0040119E |. 0F85 E5000000 jnz MD5KeyGe.00401289
004011A4 |. 384424 69 cmp byte ptr ss:[esp+0x69],al ; 判断第十位是否为-
004011A8 |. 0F85 DB000000 jnz MD5KeyGe.00401289
004011AE |. 384424 6E cmp byte ptr ss:[esp+0x6E],al ; 判断第十五位是否为-
004011B2 |. 0F85 D1000000 jnz MD5KeyGe.00401289
004011B8 |. 8B4C24 65 mov ecx,dword ptr ss:[esp+0x65]
004011BC |. 8B4424 60 mov eax,dword ptr ss:[esp+0x60]
004011C0 |. 8B5424 6A mov edx,dword ptr ss:[esp+0x6A]
004011C4 |. 894C24 14 mov dword ptr ss:[esp+0x14],ecx
004011C8 |. 894424 10 mov dword ptr ss:[esp+0x10],eax
004011CC |. 8B4424 6F mov eax,dword ptr ss:[esp+0x6F]
004011D0 |. 8D8C24 280100>lea ecx,dword ptr ss:[esp+0x128]
004011D7 |. 895424 18 mov dword ptr ss:[esp+0x18],edx
004011DB |. 51 push ecx
004011DC |. 894424 20 mov dword ptr ss:[esp+0x20],eax
004011E0 |. E8 CB000000 call MD5KeyGe.004012B0 ; Md5_Init
004011E5 |. 8D9424 4C0200>lea edx,dword ptr ss:[esp+0x24C] ; 取用户名存放地址
004011EC |. 57 push edi ; 长度
004011ED |. 8D8424 300100>lea eax,dword ptr ss:[esp+0x130]
004011F4 |. 52 push edx
004011F5 |. 50 push eax
004011F6 |. E8 E5000000 call MD5KeyGe.004012E0 ;函数内进行循环运算
004011FB |. 83C4 10 add esp,0x10
004011FE |. 8D4C24 24 lea ecx,dword ptr ss:[esp+0x24]
00401202 |. 51 push ecx ; /String = NULL
00401203 |. FF15 04604000 call dword ptr ds:[<&KERNEL32.lstrlenA>] ; \lstrlenA
00401209 |. 50 push eax
0040120A |. 8D5424 28 lea edx,dword ptr ss:[esp+0x28]
0040120E |. 8D8424 2C0100>lea eax,dword ptr ss:[esp+0x12C]
00401215 |. 52 push edx
00401216 |. 50 push eax
00401217 |. E8 C4000000 call MD5KeyGe.004012E0
0040121C |. 8D8C24 340100>lea ecx,dword ptr ss:[esp+0x134]
00401223 |. 8D9424 8C0100>lea edx,dword ptr ss:[esp+0x18C]
0040122A |. 51 push ecx
0040122B |. 52 push edx
0040122C |. E8 5F010000 call MD5KeyGe.00401390
00401231 |. 83C4 14 add esp,0x14
00401234 |. 33C0 xor eax,eax
00401236 |> 8A8C04 800100>/mov cl,byte ptr ss:[esp+eax+0x180]
0040123D |. 83E1 1F |and ecx,0x1F
00401240 |. 40 |inc eax
00401241 |. 83F8 10 |cmp eax,0x10
00401244 |. 8A540C 3C |mov dl,byte ptr ss:[esp+ecx+0x3C]
00401248 |. 889404 0F0300>|mov byte ptr ss:[esp+eax+0x30F],dl
0040124F |.^ 7C E5 \jl short MD5KeyGe.00401236
00401251 |. 8D8424 100300>lea eax,dword ptr ss:[esp+0x310]
00401258 |. 8D4C24 10 lea ecx,dword ptr ss:[esp+0x10]
0040125C |. 50 push eax ; /String2 = "#Eg壂惋簶vT2"
0040125D |. 51 push ecx ; |String1 = NULL
0040125E |. FF15 00604000 call dword ptr ds:[<&KERNEL32.lstrcmpA>] ; \lstrcmpA
00401264 |. 85C0 test eax,eax
00401266 |. 75 21 jnz short MD5KeyGe.00401289
00401268 |. 68 80704000 push MD5KeyGe.00407080 ; /Text = "Success!"
0040126D |. 68 E9030000 push 0x3E9 ; |ControlID = 3E9 (1001.)
00401272 |. 56 push esi ; |hWnd = 00090812 ('MD5 *KeyGenMe*',class='#32770')
00401273 |. FF15 A8604000 call dword ptr ds:[<&USER32.SetDlgItemTextA>] ; \SetDlgItemTextA
00401279 |. 5F pop edi ; 0019F554
0040127A |. 5E pop esi ; 0019F554
0040127B |. 5D pop ebp ; 0019F554
0040127C |. B8 01000000 mov eax,0x1
00401281 |. 5B pop ebx ; 0019F554
00401282 |. 81C4 C8030000 add esp,0x3C8
00401288 |. C3 retn
00401289 |> 68 70704000 push MD5KeyGe.00407070 ; /Text = "Wrong Serial!"
0040128E |. 68 E9030000 push 0x3E9 ; |ControlID = 3E9 (1001.)
00401293 |. 56 push esi ; |hWnd = 00090812 ('MD5 *KeyGenMe*',class='#32770')
00401294 |. FF15 A8604000 call dword ptr ds:[<&USER32.SetDlgItemTextA>] ; \SetDlgItemTextA
0040129A |. 5F pop edi ; 0019F554
0040129B |. 5E pop esi ; 0019F554
0040129C |. 5D pop ebp ; 0019F554
0040129D |. 33C0 xor eax,eax
0040129F |. 5B pop ebx ; 0019F554
004012A0 |. 81C4 C8030000 add esp,0x3C8
004012A6 \. C3 retn
Md5 Init函数:
004012B0 /$ 8B4424 04 mov eax,dword ptr ss:[esp+0x4]
004012B4 |. 33C9 xor ecx,ecx
004012B6 |. 8948 14 mov dword ptr ds:[eax+0x14],ecx
004012B9 |. 8948 10 mov dword ptr ds:[eax+0x10],ecx
004012BC |. C700 01234567 mov dword ptr ds:[eax],0x67452301
004012C2 |. C740 04 89ABC>mov dword ptr ds:[eax+0x4],0xEFCDAB89
004012C9 |. C740 08 FEDCB>mov dword ptr ds:[eax+0x8],0x98BADCFE
004012D0 |. C740 0C 76543>mov dword ptr ds:[eax+0xC],0x10325476
004012D7 \. C3 retn

由此可看出这里初始化了几个常量,可以高度怀疑是Md5算法了。

这里差不多就分析完了,写个注册机测试一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
// MD5.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <STRING.H>
#include <STDIO.H>
#include <stdlib.h>
//! 定义MD5状态数据结构类型
typedef struct{
unsigned long int state[4]; // 初始链接变量;保存16字节摘要
unsigned long int count[2]; // 明文位数
unsigned char PADDING[64]; // 填充位,最大64*8位
unsigned char buffer[64]; // 输入缓冲
}MD5_State;
//! MD5转换常量
#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21
//! F, G, H and I 基本MD5函数
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
//! 将x循环左移n位
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
//! 4轮运算中FF(第1轮), GG(第2轮), HH(第3轮), and II(第4轮)转换
#define FF(a, b, c, d, x, s, ac) { \
(a) += F ((b), (c), (d)) + (x) + (unsigned long int)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) { \
(a) += G ((b), (c), (d)) + (x) + (unsigned long int)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) { \
(a) += H ((b), (c), (d)) + (x) + (unsigned long int)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) { \
(a) += I ((b), (c), (d)) + (x) + (unsigned long int)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
/******************************************************************************/
// 名称:MD5_memcpy
// 功能:输入输出字节拷贝
// 参数:output: 指向unsigned char类型输出缓冲区
// input: 指向unsigned char类型输入缓冲区
// len: 输入数据长度(字节)
// 返回:无
/******************************************************************************/
void MD5_memcpy(unsigned char *output, unsigned char *input, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i++)
output[i] = input[i];
}
/******************************************************************************/
// 名称:MD5_memset
// 功能:对MD5运算缓冲区中填充数据
// 参数:output: 指向unsigned char类型缓冲区
// value: 数据
// len: 填充大小
// 返回:无
/******************************************************************************/
void MD5_memset(unsigned char *output, int value, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i++)
((char *)output)[i] = (char)value;
}
/******************************************************************************/
// 名称:Encode
// 功能:数据类型轮换(unsigned long char -> unsigned char)
// 参数:output: 指向unsigned char类型输出缓冲区
// input: 指向unsigned long int类型输入缓冲区
// len: 输入数据长度(字节)
// 返回:无
/******************************************************************************/
void Encode(unsigned char *output, unsigned long int *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
{
output[j] = (unsigned char)(input[i] & 0xff);
output[j + 1] = (unsigned char)((input[i] >> 8) & 0xff);
output[j + 2] = (unsigned char)((input[i] >> 16) & 0xff);
output[j + 3] = (unsigned char)((input[i] >> 24) & 0xff);
}
}
/******************************************************************************/
// 名称:Decode
// 功能:数据类型轮换(unsigned char -> unsigned long int)
// 参数:output: 指向unsigned long int类型输出缓冲区
// input: 指向unsigned char类型输入缓冲区
// len: 输入数据长度(字节)
// 返回:无
/******************************************************************************/
void Decode(unsigned long int *output, unsigned char *input, unsigned int len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
output[i] = ((unsigned long int)input[j]) | (((unsigned long int)input[j + 1]) << 8) |
(((unsigned long int)input[j + 2]) << 16) | (((unsigned long int)input[j + 3]) << 24);
}
/******************************************************************************/
// 名称:MD5Init
// 功能:初始链接变量赋值;初始化填充位
// 参数:指向MD5状态数据变量
// 返回:无
// 备注:填充位第1位为1,其余位为0
/******************************************************************************/
void MD5_Init(MD5_State *s)
{
s->count[0] = s->count[1] = 0;
//! 初始链接变量
s->state[0] = 0x67452301;
s->state[1] = 0xefcdab89;
s->state[2] = 0x98badcfe;
s->state[3] = 0x10325476;
//! 初始填充位(目标形式: 0x80000000......,共计512位)
MD5_memset(s->PADDING, 0, sizeof(s->PADDING));
*(s->PADDING) = 0x80;
// s->PADDING = {
// 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
}
/******************************************************************************/
// 名称:MD5Transform
// 功能:MD5 4轮运算
// 参数:state: 链接变量;block: 子明文分组
// 返回:无
// 备注:4轮共计64步运算
/******************************************************************************/
void MD5_Transform(unsigned long int state[4], unsigned char block[64])
{
unsigned long int a = state[0], b = state[1], c = state[2], d = state[3], x[16];
Decode(x, block, 64);
//! 第1轮
FF(a, b, c, d, x[0], S11, 0xd76aa478); // 1
FF(d, a, b, c, x[1], S12, 0xe8c7b756); // 2
FF(c, d, a, b, x[2], S13, 0x242070db); // 3
FF(b, c, d, a, x[3], S14, 0xc1bdceee); // 4
FF(a, b, c, d, x[4], S11, 0xf57c0faf); // 5
FF(d, a, b, c, x[5], S12, 0x4787c62a); // 6
FF(c, d, a, b, x[6], S13, 0xa8304613); // 7
FF(b, c, d, a, x[7], S14, 0xfd469501); // 8
FF(a, b, c, d, x[8], S11, 0x698098d8); // 9
FF(d, a, b, c, x[9], S12, 0x8b44f7af); // 10
FF(c, d, a, b, x[10], S13, 0xffff5bb1); // 11
FF(b, c, d, a, x[11], S14, 0x895cd7be); // 12
FF(a, b, c, d, x[12], S11, 0x6b901122); // 13
FF(d, a, b, c, x[13], S12, 0xfd987193); // 14
FF(c, d, a, b, x[14], S13, 0xa679438e); // 15
FF(b, c, d, a, x[15], S14, 0x49b40821); // 16
//! 第2轮
GG(a, b, c, d, x[1], S21, 0xf61e2562); // 17
GG(d, a, b, c, x[6], S22, 0xc040b340); // 18
GG(c, d, a, b, x[11], S23, 0x265e5a51); // 19
GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); // 20
GG(a, b, c, d, x[5], S21, 0xd62f105d); // 21
GG(d, a, b, c, x[10], S22, 0x2441453); // 22
GG(c, d, a, b, x[15], S23, 0xd8a1e681); // 23
GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); // 24
GG(a, b, c, d, x[9], S21, 0x21e1cde6); // 25
GG(d, a, b, c, x[14], S22, 0xc33707d6); // 26
GG(c, d, a, b, x[3], S23, 0xf4d50d87); // 27
GG(b, c, d, a, x[8], S24, 0x455a14ed); // 28
GG(a, b, c, d, x[13], S21, 0xa9e3e905); // 29
GG(d, a, b, c, x[2], S22, 0xfcefa3f8); // 30
GG(c, d, a, b, x[7], S23, 0x676f02d9); // 31
GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); // 32
//! 第3轮
HH(a, b, c, d, x[5], S31, 0xfffa3942); // 33
HH(d, a, b, c, x[8], S32, 0x8771f681); // 34
HH(c, d, a, b, x[11], S33, 0x6d9d6122); // 35
HH(b, c, d, a, x[14], S34, 0xfde5380c); // 36
HH(a, b, c, d, x[1], S31, 0xa4beea44); // 37
HH(d, a, b, c, x[4], S32, 0x4bdecfa9); // 38
HH(c, d, a, b, x[7], S33, 0xf6bb4b60); // 39
HH(b, c, d, a, x[10], S34, 0xbebfbc70); // 40
HH(a, b, c, d, x[13], S31, 0x289b7ec6); // 41
HH(d, a, b, c, x[0], S32, 0xeaa127fa); // 42
HH(c, d, a, b, x[3], S33, 0xd4ef3085); // 43
HH(b, c, d, a, x[6], S34, 0x4881d05); // 44
HH(a, b, c, d, x[9], S31, 0xd9d4d039); // 45
HH(d, a, b, c, x[12], S32, 0xe6db99e5); // 46
HH(c, d, a, b, x[15], S33, 0x1fa27cf8); // 47
HH(b, c, d, a, x[2], S34, 0xc4ac5665); // 48
//! 第4轮
II(a, b, c, d, x[0], S41, 0xf4292244); // 49
II(d, a, b, c, x[7], S42, 0x432aff97); // 50
II(c, d, a, b, x[14], S43, 0xab9423a7); // 51
II(b, c, d, a, x[5], S44, 0xfc93a039); // 52
II(a, b, c, d, x[12], S41, 0x655b59c3); // 53
II(d, a, b, c, x[3], S42, 0x8f0ccc92); // 54
II(c, d, a, b, x[10], S43, 0xffeff47d); // 55
II(b, c, d, a, x[1], S44, 0x85845dd1); // 56
II(a, b, c, d, x[8], S41, 0x6fa87e4f); // 57
II(d, a, b, c, x[15], S42, 0xfe2ce6e0); // 58
II(c, d, a, b, x[6], S43, 0xa3014314); // 59
II(b, c, d, a, x[13], S44, 0x4e0811a1); // 60
II(a, b, c, d, x[4], S41, 0xf7537e82); // 61
II(d, a, b, c, x[11], S42, 0xbd3af235); // 62
II(c, d, a, b, x[2], S43, 0x2ad7d2bb); // 63
II(b, c, d, a, x[9], S44, 0xeb86d391); // 64
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
MD5_memset((unsigned char*)x, 0, sizeof(x));
}
/******************************************************************************/
// 名称:MD5_Update
// 功能:明文填充,明文分组,16个子明文分组
// 参数:指向SHA状态变量
// 返回:无
/******************************************************************************/
void MD5_Update(MD5_State *s, unsigned char *input, unsigned int inputLen)
{
unsigned int i, index, partLen;
//! 明文填充
//! 字节数 mod 64
index = (unsigned int)((s->count[0] >> 3) & 0x3F);
//! 更新位数
if ((s->count[0] += ((unsigned long int)inputLen << 3))
< ((unsigned long int)inputLen << 3))
s->count[1]++;
s->count[1] += ((unsigned long int)inputLen >> 29);
partLen = 64 - index;
//! MD5 4轮运算
if (inputLen >= partLen)
{
MD5_memcpy((unsigned char*)&s->buffer[index], (unsigned char*)input, partLen);
MD5_Transform(s->state, s->buffer);
for (i = partLen; i + 63 < inputLen; i += 64)
MD5_Transform(s->state, &input[i]);
index = 0;
}
else
i = 0;
MD5_memcpy((unsigned char*)&s->buffer[index], (unsigned char*)&input[i], inputLen - i);
}
/******************************************************************************/
// 名称:MD5_Final
// 功能:MD5最后变换
// 参数:strContent:指向文件内容缓冲区; iLength:文件内容长度; output:摘要输出缓冲区
// 返回:无
/******************************************************************************/
void MD5_Final(MD5_State *s, unsigned char digest[16])
{
unsigned char bits[8];
unsigned int index, padLen;
Encode(bits, s->count, 8);
//! 长度小于448位(mod 512),对明文进行填充
index = (unsigned int)((s->count[0] >> 3) & 0x3f);
padLen = (index < 56) ? (56 - index) : (120 - index);
MD5_Update(s, s->PADDING, padLen);
MD5_Update(s, bits, 8);
Encode(digest, s->state, 16);
MD5_memset((unsigned char*)s, 0, sizeof(*s));
MD5_Init(s);
}
/******************************************************************************/
// 名称:SHA_digest
// 功能:生成文件摘要
// 参数:strContent:指向文件内容缓冲区; iLength:文件内容长度; output:摘要输出缓冲区
// 返回:无
/******************************************************************************/
void md5_digest(void const *strContent, unsigned int iLength, unsigned char output[16])
{
unsigned char *q = (unsigned char*)strContent;
MD5_State s;
MD5_Init(&s);
MD5_Update(&s, q, iLength);
MD5_Final(&s, output);
}
int main(int argc, char* argv[])
{
unsigned char output[16] = { 0 };
int i = 0;
//md5_digest("device0FCAA1460F12C", strlen("device0FCAA1460F12C"), output);
md5_digest("pediywww.pediy.com", strlen("pediywww.pediy.com"), output);
for (i = 0; i < 16; i++)
{
printf("%02x", output[i]);
}
printf("\r\n");
char table[0x20] = { "3456789ABCDEFGHJKLMNPQRSTUVWXYZ" };
char serial[0x20] = { 0 };
for (int i = 0; i < 0x10; i++)
{
int j = output[i] & 0x1f;
serial[i] = table[j];
}
printf(serial);
return 0;
}

测试成功。

总结一下识别Md5的关键特征:
1)四个初始化的常量0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476
2)4轮运算