java md5加密代码-java实现md5加密代码
立即加星标
每天看好文
目录
一、前言介绍
二、参数分析
三、堆栈调试
四、算法分析
五、思路总结
趣味模块
小军是一名工程师,最近小军遇到了一个棘手的问题:小军想要还原一个加密算法,他不想和往常一样通过Python调用JS的方式去实现算法还原;而是选择通过Python、Go、Java语言去实现算法还原。这篇文章中,我们将解决小军遇到的困境,让我们一起去看小军遇到的难题并通过多种语言去实现算法还原吧!
一、前言介绍
1、什么是md5加密?
MD5消息摘要算法(MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5加密是一种不可逆的加密算法,不可逆加密算法的特征是加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,只有重新输入明文,并再次经过同样不可逆的加密算法处理,得到相同的加密密文并被系统重新识别后,才能真正解密。
2、md5是如何加密的?
MD5算法的原理可简要的叙述为:MD5码以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
总体流程如下图所示,每次的运算都由前一轮的128位结果值和当前的512bit值进行运算。
了解了md5加密后,接下来我们去实战中分析md5是如何实现魔改并进行加密运算的。
二、参数分析
1、首先打开我们今天要模拟的网站,刷新当前页面,使用fn+F12打开开发者界面,直接定位我们要获取的接口java md5加密代码,截图如下所示:
2、我们确定好获取的接口后,点击payload查看该请求参数,截图如下所示:
3、标红的参数就是我们本次要还原的加密参数,接下来,我们对该接口各个参数进行初判断及整理分析:
Data参数分析:
Headers参数分析:
说明:由于headers参数没有重要参数影响,故不作说明。
ormData
三、断点调试
1、使用最简单的方式,查询指定关键字、加密方法,定位加密参数具体坐标文件,截图如下:
说明:经过查询,我们可以肯定的是代码中没有用到这个变量名,然后我们去搜索加密方法,发现能搜到结果,但是和我们的加密参数关联不大,截图如下:
2、接下来,我们还是使用XHR打断点,回溯堆栈的方式查找吧,截图如下:
3、然后刷新当前页面,进行堆栈查找,截图如下:
总结:很明显此刻加密参数已经生成,我们需要定位参数生成的位置,就需要学会查看堆栈信息,接下里进行堆栈回溯。
4、通过Call Stack进行堆栈回溯,截图如下所示:
5、由于堆栈回溯流程环节较多,我们直接快进定位到加密参数位置,截图如下:
说明:此刻我们可以看到t参数为timetamp参数拼接salt参数,然后进行下面参数运行即可得到第一次加密的密文,截图如下所示:
总结:此刻我们验证下前面的猜想:是否是md5加密,将明文信息粘贴到md5在线生成工具中验证,结果和js生成的值不匹配,截图如下:
6、继续执行断点,我们可以看到第二次加密运行截图如下图所示:
总结:此刻我们可以看到第二次加密运行的入参为:formDataStr拼接刚刚加密运行得到密文的32位字符串。继续执行断点,截图如下图所示:
7、将JS断点调试生成的最终加密值,与xhr请求时发送的formDataSign加密值对比,截图如下:
总结:我们可以看到formDataSign的值是经过两轮js自定义魔改算法而生成的java md5加密代码,接下来我们通过还原js加密算法去验证该网站是否使用的魔改md5。
四、算法还原
1、先将本次分析的js代码抠出来使用Nodejs运行,去掉一些无用代码后,完整代码如下:
function encrypt(e) {
function h(a, b) {
var c, d, e, f, g;
e = a & 2147483648;
f = b & 2147483648;
c = a & 1073741824;
d = b & 1073741824;
g = (a & 1073741823) + (b & 1073741823);
return c & d ? g ^ 2147483648 ^ e ^ f : c | d ? g & 1073741824 ? g ^ 3221225472 ^ e ^ f : g ^ 1073741824 ^ e ^ f : g ^ e ^ f
}
function k(a, b, c, d, e, f, g) {
a = h(a, h(h(b & c | ~b & d, e), g));
return h(a << f | a >>> 32 - f, b)
}
function l(a, b, c, d, e, f, g) {
a = h(a, h(h(b & d | c & ~d, e), g));
return h(a << f | a >>> 32 - f, b)
}
function m(a, b, d, c, e, f, g) {
a = h(a, h(h(b ^ d ^ c, e), g));
return h(a << f | a >>> 32 - f, b)
}
function n(a, b, d, c, e, f, g) {
a = h(a, h(h(d ^ (b | ~c), e), g));
return h(a << f | a >>> 32 - f, b)
}
function p(a) {
var b = "",
d = "",
c;
for (c = 0; 3 >= c; c++) d = a >>> 8 * c & 255, d = "0" + d.toString(16), b += d.substr(d.length - 2, 2);
return b
}
var f = [],
q, r, s, t, a, b, c, d;
e = function (a) {
a = a.replace(/\\r\\n/g, "\\n");
for (var b = "", d = 0; d < a.length; d++) {
var c = a.charCodeAt(d);
128 > c ? b += String.fromCharCode(c) : (127 < c && 2048 > c ? b += String.fromCharCode(c >> 6 | 192) : (b += String.fromCharCode(c >> 12 | 224), b += String.fromCharCode(c >> 6 & 63 | 128)), b += String.fromCharCode(c & 63 | 128))
}
return b
}(e);
f = function (b) {
var a, c = b.length;
a = c + 8;
for (var d = 16 * ((a - a % 64) / 64 + 1), e = Array(d - 1), f = 0, g = 0; g < c;) a = (g - g % 4) / 4, f = g % 4 * 8, e[a] |= b.charCodeAt(g) << f, g++;
a = (g - g % 4) / 4;
e[a] |= 128 << g % 4 * 8;
e[d - 2] = c << 3;
e[d - 1] = c >>> 29;
return e
}(e);
a = 271733878;
b = 2562383102;
c = 4023233417;
d = 1732584193;
for (e = 0; e < f.length; e += 16) q = a, r = b, s = c, t = d, a = k(a, b, c, d, f[e + 0], 7, 3614090360), d = k(d, a, b, c, f[e + 1], 12, 3905402710), c = k(c, d, a, b, f[e + 2], 17, 606105819), b = k(b, c, d, a, f[e + 3], 22, 3250441966), a = k(a, b, c, d, f[e + 4], 7, 4118548399), d = k(d, a, b, c, f[e + 5], 12, 1200080426), c = k(c, d, a, b, f[e + 6], 17, 2821735955), b = k(b, c, d, a, f[e + 7], 22, 4249261313), a = k(a, b, c, d, f[e + 8], 7, 1770035416), d = k(d, a, b, c, f[e + 9], 12, 2336552879), c = k(c, d, a, b, f[e + 10], 17, 4294925233), b = k(b, c, d, a, f[e + 11], 22, 2304563134), a = k(a, b, c, d, f[e + 12], 7, 1804603682), d = k(d, a, b, c, f[e + 13], 12, 4254626195), c = k(c, d, a, b, f[e + 14], 17, 2792965006), b = k(b, c, d, a, f[e + 15], 22, 1236535329), a = l(a, b, c, d, f[e + 1], 5, 4129170786), d = l(d, a, b, c, f[e + 6], 9, 3225465664), c = l(c, d, a, b, f[e + 11], 14, 643717713), b = l(b, c, d, a, f[e + 0], 20, 3921069994), a = l(a, b, c, d, f[e + 5], 5, 3593408605), d = l(d, a, b, c, f[e + 10], 9, 38016083), c = l(c, d, a, b, f[e + 15], 14, 3634488961), b = l(b, c, d, a, f[e + 4], 20, 3889429448), a = l(a, b, c, d, f[e + 9], 5, 568446438), d = l(d, a, b, c, f[e + 14], 9, 3275163606), c = l(c, d, a, b, f[e + 3], 14, 4107603335), b = l(b, c, d, a, f[e + 8], 20, 1163531501), a = l(a, b, c, d, f[e + 13], 5, 2850285829), d = l(d, a, b, c, f[e + 2], 9, 4243563512), c = l(c, d, a, b, f[e + 7], 14, 1735328473), b = l(b, c, d, a, f[e + 12], 20, 2368359562), a = m(a, b, c, d, f[e + 5], 4, 4294588738), d = m(d, a, b, c, f[e + 8], 11, 2272392833), c = m(c, d, a, b, f[e + 11], 16, 1839030562), b = m(b, c, d, a, f[e + 14], 23, 4259657740), a = m(a, b, c, d, f[e + 1], 4, 2763975236), d = m(d, a, b, c, f[e + 4], 11, 1272893353), c = m(c, d, a, b, f[e + 7], 16, 4139469664), b = m(b, c, d, a, f[e + 10], 23, 3200236656), a = m(a, b, c, d, f[e + 13], 4, 681279174), d = m(d, a, b, c, f[e + 0], 11, 3936430074), c = m(c, d, a, b, f[e + 3], 16, 3572445317), b = m(b, c, d, a, f[e + 6], 23, 76029189), a = m(a, b, c, d, f[e + 9], 4, 3654602809), d = m(d, a, b, c, f[e + 12], 11, 3873151461), c = m(c, d, a, b, f[e + 15], 16, 530742520), b = m(b, c, d, a, f[e + 2], 23, 3299628645), a = n(a, b, c, d, f[e + 0], 6, 4096336452), d = n(d, a, b, c, f[e + 7], 10, 1126891415), c = n(c, d, a, b, f[e + 14], 15, 2878612391), b = n(b, c, d, a, f[e + 5], 21, 4237533241), a = n(a, b, c, d, f[e + 12], 6, 1700485571), d = n(d, a, b, c, f[e + 3], 10, 2399980690), c = n(c, d, a, b, f[e + 10], 15, 4293915773), b = n(b, c, d, a, f[e + 1], 21, 2240044497), a = n(a, b, c, d, f[e + 8], 6, 1873313359), d = n(d, a, b, c, f[e + 15], 10, 4264355552), c = n(c, d, a, b, f[e + 6], 15, 2734768916), b = n(b, c, d, a, f[e + 13], 21, 1309151649), a = n(a, b, c, d, f[e + 4], 6, 4149444226), d = n(d, a, b, c, f[e + 11], 10, 3174756917), c = n(c, d, a, b, f[e + 2], 15, 718787259), b = n(b, c, d, a, f[e + 9], 21, 3951481745), a = h(a, q), b = h(b, r), c = h(c, s), d = h(d, t);
return (p(a) + p(b) + p(c) + p(d)).toLowerCase()
};
var params = "16774142142710df14fccd4ef73ee9d59451a6c2fb";
var first_md5 = encrypt(params);
var keyword = '{"keyword":"三只羊"}' + first_md5;
var last_md5 = encrypt(keyword);
console.log(last_md5);
1.1 代码运行后截图如下:
总结:通过还原js代码,我们已经能够解决小军提到的问题。大家肯定很好奇,为啥我知道该网站使用的算法是魔改md5加密算法,很简单的一步操作就是先看常量(a、b、c、d),再看码表K。很明显这个地方的a、b、c、d四个常量转为16进制后,是经过了特殊的魔改而来。接下来让我们用其他语言来实现该算法吧!
2、经过上面的深度分析后,我通过修改md5源码实现了一版Python魔改的md5算法,完整代码如下:
# -*- coding: utf-8 -*-
# -------------------------
# @author : 逆向与爬虫的故事
# -------------------------
def magic_md5(message: str) -> bytes:
# 定义常量,用于初始化128位变量,注意字节顺序,A=0x01234567,这里低值存放低字节,
# 即01 23 45 67,所以运算时A=0x67452301,其他类似。
# 用字符串的形势,是为了和hex函数的输出统一,hex(10)输出为'0xA',注意结果为字符串。
h0 = 0x10325476
h1 = 0x98badcfe
h2 = 0xefcdab89
h3 = 0x67452301
# 定义每轮中循环左移的位数,用元组表示 4*4*4=64
R = (7, 12, 17, 22) * 4 + (5, 9, 14, 20) * 4 + \
(4, 11, 16, 23) * 4 + (6, 10, 15, 21) * 4
# 定义常数K 64
# K[i] = (int(abs(math.sin(i + 1)) * 2 ** 32)) & 0xffffffff
K = (0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8,
0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193,
0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51,
0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905,
0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681,
0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60,
0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244,
0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92,
0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314,
0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391)
# 定义每轮中用到的函数。L为循环左移,
# 左移之后可能会超过32位,所以要和0xffffffff做与运算,确保结果为32位。
F = lambda x, y, z: ((x & y) | ((~x) & z))
G = lambda x, y, z: ((x & z) | (y & (~z)))
H = lambda x, y, z: (x ^ y ^ z)
I = lambda x, y, z: (y ^ (x | (~z)))
L = lambda x, n: ((x << n) | (x >> (32 - n))) & 0xffffffff
# 小端 0x12,0x34,0x56,0x78 -> 0x78563412
# 将四个8位无符号数转化为一个32位无符号数
W = lambda i4, i3, i2, i1: (i1 << 24) | (i2 << 16) | (i3 << 8) | i4
# 字节翻转 0x12345678 -> 0x78563412 将一个32位无符号数的高位和低位进行对换
reverse = lambda x: (x << 24) & 0xff000000 | (x << 8) & 0x00ff0000 | \
(x >> 8) & 0x0000ff00 | (x >> 24) & 0x000000ff
# 对每一个输入先添加一个'0x80',即'10000000', 即128
ascii_list = list(map(lambda x: x, message.encode()))
msg_length = len(ascii_list) * 8
ascii_list.append(128)
# 补充0
while (len(ascii_list) * 8 + 64) % 512 != 0:
ascii_list.append(0)
# 最后64为存放消息长度,以小端数存放。
# 例如,消息为'a',则长度是8,则添加'0x0800000000000000'
for i in range(8):
ascii_list.append((msg_length >> (8 * i)) & 0xff)
# print(ascii_list)
# print(len(ascii_list)//64)
# 对每一消息块进行迭代
for i in range(len(ascii_list) // 64):
# print(ascii_list[i*64:(i+1)*64])
# 对每一个消息块进行循环,每个消息块512bits=16*32bits=64*8bits
a, b, c, d = h0, h1, h2, h3
for j in range(64):
# 64轮的主循环
if 0 <= j <= 15:
f = F(b, c, d) & 0xffffffff
g = j
elif 16 <= j <= 31:
f = G(b, c, d) & 0xffffffff
g = ((5 * j) + 1) % 16
elif 32 <= j <= 47:
f = H(b, c, d) & 0xffffffff
g = ((3 * j) + 5) % 16
else:
f = I(b, c, d) & 0xffffffff
g = (7 * j) % 16
aa, dd, cc = d, c, b
# 第i个chunk,第g个32-bit
s = i * 64 + g * 4
w = W(ascii_list[s], ascii_list[s + 1], ascii_list[s + 2], ascii_list[s + 3])
bb = (L((a + f + K[j] + w) & 0xffffffff, R[j]) + b) & 0xffffffff
a, b, c, d = aa, bb, cc, dd
# print(b)
h0 = (h0 + a) & 0xffffffff
h1 = (h1 + b) & 0xffffffff
h2 = (h2 + c) & 0xffffffff
h3 = (h3 + d) & 0xffffffff
h0, h1, h2, h3 = reverse(h0), reverse(h1), reverse(h2), reverse(h3)
digest = (h0 << 96) | (h1 << 64) | (h2 << 32) | h3
return hex(digest)[2:].rjust(32, '0')
2.1 代码运行后截图如下:
3、为了满足小军的需求,我们又实现了一版Go语言版本的魔改md5算法,完整代码如下:
package md5
import (
"crypto"
"encoding/binary"
"errors"
"hash"
)
func init() {
crypto.RegisterHash(crypto.MD5, New)
}
// The size of an MD5 checksum in bytes.
const Size = 16
// The blocksize of MD5 in bytes.
const BlockSize = 64
const (
init0 = 0x10325476
init1 = 0x98badcfe
init2 = 0xefcdab89
init3 = 0x67452301
)
// digest represents the partial evaluation of a checksum.
type digest struct {
s [4]uint32
x [BlockSize]byte
nx int
len uint64
}
func (d *digest) Reset() {
d.s[0] = init0
d.s[1] = init1
d.s[2] = init2
d.s[3] = init3
d.nx = 0
d.len = 0
}
const (
magic = "md5\x01"
marshaledSize = len(magic) + 4*4 + BlockSize + 8
)
func (d *digest) MarshalBinary() ([]byte, error) {
b := make([]byte, 0, marshaledSize)
b = append(b, magic...)
b = appendUint32(b, d.s[0])
b = appendUint32(b, d.s[1])
b = appendUint32(b, d.s[2])
b = appendUint32(b, d.s[3])
b = append(b, d.x[:d.nx]...)
b = b[:len(b)+len(d.x)-d.nx] // already zero
b = appendUint64(b, d.len)
return b, nil
}
func (d *digest) UnmarshalBinary(b []byte) error {
if len(b) < len(magic) || string(b[:len(magic)]) != magic {
return errors.New("crypto/md5: invalid hash state identifier")
}
if len(b) != marshaledSize {
return errors.New("crypto/md5: invalid hash state size")
}
b = b[len(magic):]
b, d.s[0] = consumeUint32(b)
b, d.s[1] = consumeUint32(b)
b, d.s[2] = consumeUint32(b)
b, d.s[3] = consumeUint32(b)
b = b[copy(d.x[:], b):]
b, d.len = consumeUint64(b)
d.nx = int(d.len % BlockSize)
return nil
}
func appendUint64(b []byte, x uint64) []byte {
var a [8]byte
binary.BigEndian.PutUint64(a[:], x)
return append(b, a[:]...)
}
func appendUint32(b []byte, x uint32) []byte {
var a [4]byte
binary.BigEndian.PutUint32(a[:], x)
return append(b, a[:]...)
}
func consumeUint64(b []byte) ([]byte, uint64) {
return b[8:], binary.BigEndian.Uint64(b[0:8])
}
func consumeUint32(b []byte) ([]byte, uint32) {
return b[4:], binary.BigEndian.Uint32(b[0:4])
}
// New returns a new hash.Hash computing the MD5 checksum. The Hash also
// implements encoding.BinaryMarshaler and encoding.BinaryUnmarshaler to
// marshal and unmarshal the internal state of the hash.
func New() hash.Hash {
d := new(digest)
d.Reset()
return d
}
func (d *digest) Size() int { return Size }
func (d *digest) BlockSize() int { return BlockSize }
func (d *digest) Write(p []byte) (nn int, err error) {
// Note that we currently call block or blockGeneric
// directly (guarded using haveAsm) because this allows
// escape analysis to see that p and d don't escape.
nn = len(p)
d.len += uint64(nn)
if d.nx > 0 {
n := copy(d.x[d.nx:], p)
d.nx += n
if d.nx == BlockSize {
if haveAsm {
block(d, d.x[:])
} else {
blockGeneric(d, d.x[:])
}
d.nx = 0
}
p = p[n:]
}
if len(p) >= BlockSize {
n := len(p) &^ (BlockSize - 1)
if haveAsm {
block(d, p[:n])
} else {
blockGeneric(d, p[:n])
}
p = p[n:]
}
if len(p) > 0 {
d.nx = copy(d.x[:], p)
}
return
}
func (d *digest) Sum(in []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
d0 := *d
hash := d0.checkSum()
return append(in, hash[:]...)
}
func (d *digest) checkSum() [Size]byte {
// Append 0x80 to the end of the message and then append zeros
// until the length is a multiple of 56 bytes. Finally append
// 8 bytes representing the message length in bits.
//
// 1 byte end marker :: 0-63 padding bytes :: 8 byte length
tmp := [1 + 63 + 8]byte{0x80}
pad := (55 - d.len) % 64 // calculate number of padding bytes
binary.LittleEndian.PutUint64(tmp[1+pad:], d.len<<3) // append length in bits
d.Write(tmp[:1+pad+8])
// The previous write ensures that a whole number of
// blocks (i.e. a multiple of 64 bytes) have been hashed.
if d.nx != 0 {
panic("d.nx != 0")
}
var digest [Size]byte
binary.LittleEndian.PutUint32(digest[0:], d.s[0])
binary.LittleEndian.PutUint32(digest[4:], d.s[1])
binary.LittleEndian.PutUint32(digest[8:], d.s[2])
binary.LittleEndian.PutUint32(digest[12:], d.s[3])
return digest
}
// Sum returns the MD5 checksum of the data.
func Sum(data []byte) [Size]byte {
var d digest
d.Reset()
d.Write(data)
return d.checkSum()
}
3.1 main函数完整代码如下:
package main
import (
"collyx-spider/utils/md5"
"fmt"
)
func main() {
firstText := "16774142142710df14fccd4ef73ee9d59451a6c2fb"
signBytes := md5.Sum([]byte(firstText))
sign := fmt.Sprintf("%x", signBytes)
fmt.Println(sign)
secondText := `{"keyword":"三只羊"}` + sign
sign2Bytes := md5.Sum([]byte(secondText))
lastSign := fmt.Sprintf("%x", sign2Bytes)
fmt.Println(lastSign)
}
3.2 代码运行后,截图如下所示:
总结:观察Goland生成的加密值,我们可以确定和前面计算的结果一致,接下来我们再研究下java版本魔改md5如何实现。
4、作者通过Java语言实现的魔改md5完整代码如下:
import java.util.Arrays;
public class MagicMd5 {
private String content;
private String md5;
public String md5(String text) {
content = text;
this.Reset();
return md5;
}
private void Reset() {
int[] init = new int[]{0x10325476, 0x98badcfe, 0xefcdab89, 0x67452301};
byte[] bytes = new byte[64];
byte[] tail = new byte[0];
int len = 0;
long size = 0;
byte[] src = content.getBytes();
int n = src.length / 64;
for (int i = 0; i < n; i++) {
bytes = Arrays.copyOfRange(src, i * 64, i * 64 + 64);
init = md5_2(bytes, init);
}
size = src.length * 8;
tail = Arrays.copyOfRange(src, src.length - src.length % 64, src.length);
if (tail.length < 56) {
bytes = Arrays.copyOf(tail, 64);
bytes[tail.length] = -128;
} else {
bytes = Arrays.copyOf(tail, 64);
bytes[tail.length] = -128;
init = md5_2(bytes, init);
bytes = Arrays.copyOf(new byte[]{}, 64);
}
for (int i = 0; i < 8; i++) {
bytes[56 + i] = new Long(size >>> i * 8).byteValue();
}
init = md5_2(bytes, init);
md5 = int2string(init[0]) + int2string(init[1]) + int2string(init[2]) + int2string(init[3]);
}
public int[] md5_2(byte[] bytes, int[] before) {
int A = before[0], B = before[1], C = before[2], D = before[3];
int a = A, b = B, c = C, d = D;
int s[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11,
16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15,
21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
int[] k = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf,
0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af,
0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e,
0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6,
0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8,
0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122,
0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039,
0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97,
0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d,
0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
for (int i = 0; i < 64; i++) {
int f, g;
if (i < 16) {
f = (b & c) | (~b & d);
g = i;
} else if (i < 32) {
f = (b & d) | (~d & c);
g = (5 * i + 1) % 16;
} else if (i < 48) {
f = b ^ c ^ d;
g = (3 * i + 5) % 16;
} else {
f = c ^ (~d | b);
g = 7 * i % 16;
}
int m = byteArr2Int(Arrays.copyOfRange(bytes, 4 * g, 4 * g + 4));
int b_temp = b;
b = b + Integer.rotateLeft(a + f + m + k[i], s[i]);
a = d;
d = c;
c = b_temp;
}
A += a;
B += b;
C += c;
D += d;
int[] res = new int[4];
res[0] = A;
res[1] = B;
res[2] = C;
res[3] = D;
return res;
}
public String int2string(int n) {
String res = "";
for (int i = 0; i < 4; i++) {
String s = (Integer.toHexString((n >>> (8 * i)) & 0xff));
if (s.length() < 2) {
s = "0" + s;
}
res += s;
}
return res;
}
private int byteArr2Int(byte[] bytes) {
int res = 0;
for (int i = 3; i >= 0; i--) {
res = (res << 8);
res += (int) bytes[i] & 0xff;
}
return res;
}
@Override
public String toString() {
return "MD5 [content=" + content + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((content == null) ? 0 : content.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MagicMd5 other = (MagicMd5) obj;
if (content == null) {
if (other.content != null)
return false;
} else if (!content.equals(other.content))
return false;
return true;
}
public static void main(String[] args) {
// 运行魔改md5加密算法
MagicMd5 magicmd5 = new MagicMd5();
String firstSign = magicmd5.md5("16774142142710df14fccd4ef73ee9d59451a6c2fb");
String keyword = "{\"keyword\":\"三只羊\"}";
System.out.println(firstSign);
String lastSign = magicmd5.md5(keyword+firstSign);
System.out.println(lastSign);
}
}
4.1 代码实现后,我们将运行后的代码截图如下所示:
总结:本篇文章到这里,我们已经能够通过Js、Python、Go、Java语言去实现魔改md5算法还原了,小军遇到的难题我们已经迎刃而解,整篇文章字数有点多,感谢大家耐心观看!
五、思路总结
回顾整个分析流程,本次难点主要概括为以下几点: