被魔改md5加密坑了?某网站魔改md5加密逆向还原 (多种语言还原)

2023-10-28

大家好,我是TheWeiJun;最近由于工作太忙好久没有更新了。静下心来,突然很想念各位读者朋友,所以晚上抽空更新一篇。今天分享一篇关于魔改md5实现的加密算法逆向分析,本文将用多种语言还原加密算法,解决不同语言还原加密算法的难题。希望各位朋友能够多多提出宝贵意见,在阅读的同时记得给我一个star!

特别声明:本公众号文章只作为学术研究,不作为其它不法用途;如有侵权请联系作者删除。

目录

一、前言介绍

二、参数分析

三、堆栈调试

四、算法分析

五、思路总结

趣味模块

小军是一名工程师,最近小军遇到了一个棘手的问题:小军想要还原一个加密算法,他不想和往常一样通过Python调用JS的方式去实现算法还原;而是选择通过Python、Go、Java语言去实现算法还原。这篇文章中,我们将解决小军遇到的困境,让我们一起去看小军遇到的难题并通过多种语言去实现算法还原吧!

逆向与爬虫的故事

专注于网络爬虫、JS逆向、APP逆向、安全攻防实战经验分享及总结。


一、前言介绍

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打开开发者界面,直接定位我们要获取的接口,截图如下所示:

2、我们确定好获取的接口后,点击payload查看该请求参数,截图如下所示:

3、标红的参数就是我们本次要还原的加密参数,接下来,我们对该接口各个参数进行初判断及整理分析:

Data参数分析:

  • formDataSign     初步怀疑是md5加密,长度32位

  • formDataStr       搜索的关键字

  • jsVer                   JS发版时间

  • timestamp          当前时间戳,长度10位

Headers参数分析:

说明:由于headers参数没有重要参数影响,故不作说明。


三、断点调试

1、使用最简单的方式,查询指定关键字、加密方法,定位加密参数具体坐标文件,截图如下:

说明:经过查询,我们可以肯定的是代码中没有用到这个变量名,然后我们去搜索加密方法,发现能搜到结果,但是和我们的加密参数关联不大,截图如下:

2、接下来,我们还是使用XHR打断点,回溯堆栈的方式查找吧,截图如下:

3、然后刷新当前页面,进行堆栈查找,截图如下:

总结:很明显此刻加密参数已经生成,我们需要定位参数生成的位置,就需要学会查看堆栈信息,接下里进行堆栈回溯。

4、通过Call Stack进行堆栈回溯,截图如下所示:

5、由于堆栈回溯流程环节较多,我们直接快进定位到加密参数位置,截图如下:

说明:此刻我们可以看到t参数为timetamp参数拼接salt参数,然后进行下面参数运行即可得到第一次加密的密文,截图如下所示:

总结:此刻我们验证下前面的猜想:是否是md5加密,将明文信息粘贴到md5在线生成工具中验证,结果和js生成的值不匹配,截图如下:

6、继续执行断点,我们可以看到第二次加密运行截图如下图所示:

总结:此刻我们可以看到第二次加密运行的入参为:formDataStr拼接刚刚加密运行得到密文的32位字符串。继续执行断点,截图如下图所示:

7、将JS断点调试生成的最终加密值,与xhr请求时发送的formDataSign加密值对比,截图如下:

总结:我们可以看到formDataSign的值是经过两轮js自定义魔改算法而生成的,接下来我们通过还原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 md5import (  "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 = 64const (  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 mainimport (  "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算法还原了,小军遇到的难题我们已经迎刃而解,整篇文章字数有点多,感谢大家耐心观看!


五、思路总结

回顾整个分析流程,本次难点主要概括为以下几点:

  • 如何快速确定位加密参数的位置

  • 堆栈回源如何过滤无用代码

  • Js、Python还原加密算法实现

  • Go、Java还原加密算法实现

  • 熟练掌握Md5算法及加密运算过程

本篇分享到这里就结束了,欢迎大家关注下期,我们不见不散☀️☀️

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

被魔改md5加密坑了?某网站魔改md5加密逆向还原 (多种语言还原) 的相关文章

随机推荐

  • 《Learning Spark》第八章:调优及调试spark应用

    2020 07 05 引言 我记得当时我就是因为使用hadoop太过费劲了 才上手的spark 然后因为自己的机器性能不行 又一点一点调优 当时调优的过程 主要是从底层的结构上来进行调优 主要就是那些worker数量以及内存大小等等 但是对
  • PyTorch和TensorFlow生成对抗网络学习MNIST数据集

    介绍 生成对抗网络 简称GAN 是最近开发的最受欢迎的机器学习算法之一 对于人工智能 AI 领域的新手 我们可以简单地将机器学习 ML 描述为AI的子领域 它使用数据来 教 机器 程序如何执行新任务 一个简单的例子就是使用一个人的脸部图像作
  • png四通道透明背景图成功加入到视频帧中 使用了mask原理

    import cv2 import ffmpeg import cv2 import numpy as np import glob video f D CCTV CCTV mp4 视频文件名 output f D CCTV logoaft
  • 共享内存---结构体使用

    共享内存主要是通过映射机制实现的 Windows 下进程的地址空间在逻辑上是相互隔离的 但在物理上却是重叠的 所谓的重叠是指同一块内存区域可能被多个进程同时使用 当调用 CreateFileMapping 创建命名的内存映射文件对象时 Wi
  • 网络基础-应用层:E-mail应用:SMTP协议,POP协议,IMAP协议

    Email应用的构成 邮件客户端 邮件服务器 SMTP协议 只支持文本 邮件服务器 邮箱 存储发给该用户的Email 消息队列 存储等待发送的Email SMTP协议 邮件服务器之间传递消息所使用的协议 客户端 发送消息的服务器 服务器 接
  • js实用方法记录-js动态加载css、js脚本文件

    js实用方法记录 动态加载css js 附送一个加载iframe h5打开app代码 1 动态加载js文件到head标签并执行回调 方法调用 dynamicLoadJs http www yimo link static js main m
  • 双向链表实现简单的增删查改

    前言 上次分享了单向链表的增删查改 这次要介绍双向链表的增删查改 其实双向链表也有多种 这次主要介绍结构最复杂但是实现起功能反而最简单的带头双向循环链表 希望我的分享对各位有些许帮助 学习这篇文章的内容最好有这篇文章的基础 目录 一 双向链
  • VBA常用语法(一)

    VBA语句 一 VBA语句 1 宏程序语句 运行后可 以完成一个功能 给单元格a1赋值 Sub test 开始语句 Range a1 10 程序主体 End Sub 结束语句 2 函数程序语句 运行后可以返回一个值 Function shc
  • 阿里云数据库 MongoDB 版Python 连接示例

    安装pymongo import uuid from pymongo import MongoClient 两地址 CONN ADDR1 demotest 1 mongodb tbc3 newtest rdstest aliyun inc
  • 即将开班「中国图象图形学学会」前沿讲习班第2期——智能驾驶与机器视觉

    CSIG图像图形学科前沿讲习班第2期 主题 智能驾驶与机器视觉 2017年7月15日 17日 清华大学 智能驾驶无疑开启了交通运输行业的新时代 随着传感器和人工智能技术的逐渐成熟 智能驾驶已经从 概念化 进入 落地实用化 的关键窗口期 越来
  • 高精度地图定位在高速公路自动驾驶系统中的应用

    摘要 自动驾驶已经成为全球汽车产业的战略发展方向 其中L3 级高速公路自动驾驶是最有可能率先落地的自动驾驶系统 高精度地图和定位系统是自动驾驶系统的关键一部分 近年来发展迅速 已经达到可量产状态 文章首先分析了自动驾驶和高精度地图定位的发展
  • 运动目标检测代码(帧差、高斯混合、vibe代码实现)

    主要介绍四种运动目标检测的算法代码 每段代码博主实测可运行 当前主流的混合高斯背景模型 VIBE算法代码转载自他处 另外GMG算法 KNN算法在朱伟的书中也有讲 opencv3 0中 有专门的背景模型类BackgroundSubtracto
  • Java学习笔记29——字节流2

    字节流读数据 字节流读数据 一次读一个字节的数据 一次读一个字节数组的数据 字节流复制图片 字节流读数据 一次读一个字节的数据 FileInputStream 从文件系统中的文件获取输入字节 public class FileInputSt
  • 数据结构-第1章 概述

    第1章 概述 第2章 线性表 第3章 栈和队列 第4章 串 矩阵和广义表 第5章 树和二叉树 第6章 图 第7章 查找 第8章 排序 一 第1章 概述 1 数据 所有能被计算机识别 存储和处理的符号的集合 包括数字 字符 声音 图像等信息
  • 如何强制性管控代码质量

    一 背景 在之前的文章中 我们已经描述了jenkins和sonarqube是如何集成起来的 今天我们在该篇文章中描述下 如何使用gerrit jenkins sonar进行代码质量管控 主要是利用sonarqube分析代码的能力来管控 一般
  • ajax后台返回数据中文乱码_透彻分析和解决一切javaWeb项目乱码问题

    前言 乱码是我们在程序开发中经常碰到且让人头疼的一件事 尤其是我们在做javaweb开发 如果我们没有清楚乱码产生的原理 碰到乱码问题了就容易摸不着头脑 无从下手 乱码主要出现在两部分 如下 第一 浏览器通过表单提交到后台 如果表单内容有中
  • 近期面试总结

    最近两个月大大小小的面试已经不下十来场了 一共面了5 6个公司 大的有蚂蚁金服 小的有初创公司 也有做直播的等等 但是面试都是大同小异 因此来记录一下自己的不足之处以及需要加强的地方 项目 个人的项目经验其实非常重要 很多面试官往往会从项目
  • xprop

    xprop 使用说明 名字 xprop X的属性显示器 概要 xprop help grammar id id root name name frame font font display display len n notype fs f
  • UVM中的phase机制

    5 1 phase机制 5 1 1 task phase与function phase UVM中的phase 按照其是否消耗仿真时间 time打印出的时间 的特性 可以分成两大类 一类是function phase 如build phase
  • 被魔改md5加密坑了?某网站魔改md5加密逆向还原 (多种语言还原)

    大家好 我是TheWeiJun 最近由于工作太忙好久没有更新了 静下心来 突然很想念各位读者朋友 所以晚上抽空更新一篇 今天分享一篇关于魔改md5实现的加密算法逆向分析 本文将用多种语言还原加密算法 解决不同语言还原加密算法的难题 希望各位