[Hundreds of JS Reverse Cases] The seventh question of the anti-climbing practice platform for netizens: First experience of JSVMPZL

Pay attention to the WeChat public account: Brother K crawler, continue to share technical dry goods such as advanced crawler, JS/Android reverse!

statement

All content in this article is for learning and exchange only. The content of the captured packets, sensitive URLs, and data interfaces have been desensitized. Commercial and illegal uses are strictly prohibited. Otherwise, all consequences arising therefrom have nothing to do with the author. , please contact me on the official account to delete it immediately!

reverse goal

  • Goal: Anti-anti-reptile practice platform for net losers. Question 7: First experience of JSVMPZL
  • Link: http://spider.wangluozhe.com/challenge/7
  • Introduction: Platform registration requires an invitation code. If the webmaster is in the group, he can reply to the exchange group in the background and add the group to get it, or directly add the webmaster QQ at the bottom of the website to get it. It is required to collect all the numbers on 100 pages and sum all the data. The main difficulty lies in the JS obfuscation framework developed by vvv boss: jsvmpzl

01

reverse process

Direct search, or follow the stack, you can easily find the encrypted entry, open F12, there are two anti-debugging, one is infinite debugger, right click Never pause here, the other is timer, console input can be for (let i = 1; i < 99999; i++) window.clearInterval(i);passed .

02

If you follow up y__(), you can see the obfuscated code of jsvmpzl. If you have done a question on the ape-man study platform, you will find that this confusion is the same as the ape-man study question 18 (https://match.yuanrenxue.com/match/18) Yes, at the next breakpoint on the y__() first line, observe __v_()the first parameter _, _[2][0]you will find some features about the MD5 algorithm, as shown in the following figure:

03

So let's make a bold guess, is it that _signaturea ? Continue to debug again and notice argumentsthe changes:

04

Obviously this window.byted_acrawler(window.sign())should be the generated _signaturestatement. This method has the same name as the _signaturegenerated You can get the value by directly outputting it on the console, where window.sign()is the timestamp taken:

05

We guessed that it was MD5, and we directly verified it and found that it was not. Even if it is the same timestamp, the value obtained window.byted_acrawler()after be different every time:

06

Hook key method

After the previous analysis, since the standard MD5 does not work, is it possible that it is a magic-modified MD5? First look for a JavaScript standard MD5 code, such as: http://pajhome.org.uk/crypt/md5/md5.html

07

It can be noticed that there are many methods of , , , and , in the source code md5_ff, md5_ggand md5_hhthe md5_iilast value is fixed. Is it possible that some default values ​​are modified on the basis of standard MD5? So we can directly hook these key methods, output the incoming values ​​on the console, and compare them one by one to see if the default values ​​are the same. For the convenience of observation, we can also add colors to the output statements. The Hook code is as follows :

let oldFF = _[2][0]['md5_ff'];
let oldGG = _[2][0]['md5_gg'];
let oldHH = _[2][0]['md5_hh'];
let oldII = _[2][0]['md5_ii'];

let color_white_red = "color: white; background: red;"
let color_white_grey = "color: white; background: grey;"
let color_white_darkcyan = "color: white; background: darkcyan;"
let color_white_green = "color: white; background: green;"
let color_white_orange = "color: white; background: orange;"

_[2][0]['md5_ff'] = function (a, b, c, d, e, f, g) {
    debugger;
    let result = oldFF(a, b, c, d, e, f, g);
    console.log("%c Function: %c md5_ff %c Result: %c %s %c Params: %c %s, %s, %s, %s, %s, %s, %s ", color_white_red, color_white_grey, color_white_red, color_white_grey, result, color_white_red, color_white_grey, a, b, c, d, e, f, g)
    return result;
};

_[2][0]['md5_gg'] = function (a, b, c, d, e, f, g) {
    debugger;
    let result = oldGG(a, b, c, d, e, f, g);
    console.log("%c Function: %c md5_gg %c Result: %c %s %c Params: %c %s, %s, %s, %s, %s, %s, %s ", color_white_red, color_white_darkcyan, color_white_red, color_white_darkcyan, result, color_white_red, color_white_darkcyan, a, b, c, d, e, f, g)
    return result;
};

_[2][0]['md5_hh'] = function (a, b, c, d, e, f, g) {
    debugger;
    let result = oldHH(a, b, c, d, e, f, g);
    console.log("%c Function: %c md5_hh %c Result: %c %s %c Params: %c %s, %s, %s, %s, %s, %s, %s ", color_white_red, color_white_green, color_white_red, color_white_green, result, color_white_red, color_white_green, a, b, c, d, e, f, g)
    return result;
};

_[2][0]['md5_ii'] = function (a, b, c, d, e, f, g) {
    debugger;
    let result = oldII(a, b, c, d, e, f, g);
    console.log("%c Function: %c md5_ii %c Result: %c %s %c Params: %c %s, %s, %s, %s, %s, %s, %s ", color_white_red, color_white_orange, color_white_red, color_white_orange, result, color_white_red, color_white_orange, a, b, c, d, e, f, g)
    return result;
};

The Hook code is relatively rigid. Those who are familiar with JS can optimize it by themselves. Pay attention to the timing of injecting the code. After clearing the timer, run the breakpoint to the y__()method and then inject it. Then cancel the breakpoint and continue to the next step. The platform sees the output parameters, as shown in the following figure:

08

09

Compared with the default parameters, it can be found md5_hh()that two default parameters have been modified:

10

The default is -722521979changed -722521939, 76029189changed 76029185, and the local code can be modified:

/* ==================================
# @Time    : 2021-12-23
# @Author  : 微信公众号:K哥爬虫
# @FileName: challenge_7.js
# @Software: PyCharm
# ================================== */


/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.
 */

/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 0;   /* hex output format. 0 - lowercase; 1 - uppercase        */
var b64pad  = "";  /* base-64 pad character. "=" for strict RFC compliance   */

/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_md5(s)    { return rstr2hex(rstr_md5(str2rstr_utf8(s))); }
function b64_md5(s)    { return rstr2b64(rstr_md5(str2rstr_utf8(s))); }
function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); }
function hex_hmac_md5(k, d)
  { return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function b64_hmac_md5(k, d)
  { return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function any_hmac_md5(k, d, e)
  { return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); }

/*
 * Perform a simple self-test to see if the VM is working
 */
function md5_vm_test()
{
  return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72";
}

/*
 * Calculate the MD5 of a raw string
 */
function rstr_md5(s)
{
  return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
}

/*
 * Calculate the HMAC-MD5, of a key and some data (raw strings)
 */
function rstr_hmac_md5(key, data)
{
  var bkey = rstr2binl(key);
  if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8);

  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++)
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
  return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
}

/*
 * Convert a raw string to a hex string
 */
function rstr2hex(input)
{
  try { hexcase } catch(e) { hexcase=0; }
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var output = "";
  var x;
  for(var i = 0; i < input.length; i++)
  {
    x = input.charCodeAt(i);
    output += hex_tab.charAt((x >>> 4) & 0x0F)
           +  hex_tab.charAt( x        & 0x0F);
  }
  return output;
}

/*
 * Convert a raw string to a base-64 string
 */
function rstr2b64(input)
{
  try { b64pad } catch(e) { b64pad=''; }
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var output = "";
  var len = input.length;
  for(var i = 0; i < len; i += 3)
  {
    var triplet = (input.charCodeAt(i) << 16)
                | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
                | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > input.length * 8) output += b64pad;
      else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
    }
  }
  return output;
}

/*
 * Convert a raw string to an arbitrary string encoding
 */
function rstr2any(input, encoding)
{
  var divisor = encoding.length;
  var i, j, q, x, quotient;

  /* Convert to an array of 16-bit big-endian values, forming the dividend */
  var dividend = Array(Math.ceil(input.length / 2));
  for(i = 0; i < dividend.length; i++)
  {
    dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
  }

  /*
   * Repeatedly perform a long division. The binary array forms the dividend,
   * the length of the encoding is the divisor. Once computed, the quotient
   * forms the dividend for the next step. All remainders are stored for later
   * use.
   */
  var full_length = Math.ceil(input.length * 8 /
                                    (Math.log(encoding.length) / Math.log(2)));
  var remainders = Array(full_length);
  for(j = 0; j < full_length; j++)
  {
    quotient = Array();
    x = 0;
    for(i = 0; i < dividend.length; i++)
    {
      x = (x << 16) + dividend[i];
      q = Math.floor(x / divisor);
      x -= q * divisor;
      if(quotient.length > 0 || q > 0)
        quotient[quotient.length] = q;
    }
    remainders[j] = x;
    dividend = quotient;
  }

  /* Convert the remainders to the output string */
  var output = "";
  for(i = remainders.length - 1; i >= 0; i--)
    output += encoding.charAt(remainders[i]);

  return output;
}

/*
 * Encode a string as utf-8.
 * For efficiency, this assumes the input is valid utf-16.
 */
function str2rstr_utf8(input)
{
  var output = "";
  var i = -1;
  var x, y;

  while(++i < input.length)
  {
    /* Decode utf-16 surrogate pairs */
    x = input.charCodeAt(i);
    y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
    if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
    {
      x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
      i++;
    }

    /* Encode output as utf-8 */
    if(x <= 0x7F)
      output += String.fromCharCode(x);
    else if(x <= 0x7FF)
      output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
                                    0x80 | ( x         & 0x3F));
    else if(x <= 0xFFFF)
      output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
                                    0x80 | ((x >>> 6 ) & 0x3F),
                                    0x80 | ( x         & 0x3F));
    else if(x <= 0x1FFFFF)
      output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
                                    0x80 | ((x >>> 12) & 0x3F),
                                    0x80 | ((x >>> 6 ) & 0x3F),
                                    0x80 | ( x         & 0x3F));
  }
  return output;
}

/*
 * Encode a string as utf-16
 */
function str2rstr_utf16le(input)
{
  var output = "";
  for(var i = 0; i < input.length; i++)
    output += String.fromCharCode( input.charCodeAt(i)        & 0xFF,
                                  (input.charCodeAt(i) >>> 8) & 0xFF);
  return output;
}

function str2rstr_utf16be(input)
{
  var output = "";
  for(var i = 0; i < input.length; i++)
    output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
                                   input.charCodeAt(i)        & 0xFF);
  return output;
}

/*
 * Convert a raw string to an array of little-endian words
 * Characters >255 have their high-byte silently ignored.
 */
function rstr2binl(input)
{
  var output = Array(input.length >> 2);
  for(var i = 0; i < output.length; i++)
    output[i] = 0;
  for(var i = 0; i < input.length * 8; i += 8)
    output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
  return output;
}

/*
 * Convert an array of little-endian words to a string
 */
function binl2rstr(input)
{
  var output = "";
  for(var i = 0; i < input.length * 32; i += 8)
    output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
  return output;
}

/*
 * Calculate the MD5 of an array of little-endian words, and a bit length.
 */
function binl_md5(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << ((len) % 32);
  x[(((len + 64) >>> 9) << 4) + 14] = len;

  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;

    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);

    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
    // 注释掉的是默认值
    // c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521939);
    // b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029185);
    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
  }
  return Array(a, b, c, d);
}

/*
 * These functions implement the four basic operations the algorithm uses.
 */
function md5_cmn(q, a, b, x, s, t)
{
  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left.
 */
function bit_rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}

function getSignature() {
    return hex_md5(Date.parse(Date()).toString())
}

console.log(getSignature())

Python calling code:

# ==================================
# --*-- coding: utf-8 --*--
# @Time    : 2021-12-23
# @Author  : 微信公众号:K哥爬虫
# @FileName: challenge_7.py
# @Software: PyCharm
# ==================================


import time
import execjs
import requests


challenge_api = "http://spider.wangluozhe.com/challenge/api/7"
headers = {
    "Cookie": "Cookie 替换成你的",
    "Host": "spider.wangluozhe.com",
    "Origin": "http://spider.wangluozhe.com",
    "Referer": "http://spider.wangluozhe.com/challenge/7",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36",
    "X-Requested-With": "XMLHttpRequest"
}


def get_signature():
    now = str(int(time.time())) + "000"
    with open('challenge_7.js', 'r', encoding='utf-8') as f:
        wlz_js = execjs.compile(f.read())
    # signature = wlz_js.call("getSignature")
    signature = wlz_js.call("hex_md5", now)
    print("signature: ", signature)
    return signature


def main():
    result = 0
    for page in range(1, 101):
        data = {
            "page": page,
            "count": 10,
            "_signature": get_signature()
        }
        response = requests.post(url=challenge_api, headers=headers, data=data).json()
        print(response)
        for d in response["data"]:
            result += d["value"]
    print("结果为: ", result)


if __name__ == '__main__':
    main()

There is a little detail here. If it is the hex_md5method , make sure that the last three digits of the timestamp are 0, otherwise the verification will fail. The usual way of writing is str(int(time.time() * 1000))to change it here: str(int(time.time())) + "000", without Python If so, you can hex_md5(Date.parse(Date()).toString())also .

Another problem is that if the MD5 code you are looking for is not standardized, to be precise, it is not the same as the MD5 code used in the title, there may be more than two places to be changed locally, so try to find a method name with both The same JS can save a lot of things.

Log breakpoints/instrumentation debugging

In addition to hooks, we can also print out _signaturethe , and reversely analyze its logic. PS: Instrumentation, that is, log breakpoint, right-click to select Add logpointto add a log breakpoint, which is equivalent to console.log()this function is newly added in Chrome 73 version.

11

  • Add breakpoint : add a normal breakpoint;
  • Add conditional breakpoint : Add a conditional breakpoint, and only break if the condition is met;
  • Add logpoint : add log breakpoint;
  • Never pause here : never stop here;
  • Add script to ignore list : Part of the JS of jquery.min.jsthe library files such as , we don't want to enter the file when we single-step debugging, so we can right-click to add and ignore such files.

There are three key log breakpoints:

  • Line 605 argumentsis the parameter value of the current incoming function;
  • Line 141, ___.join(vV_)is the name of the currently called method, and also outputs the parameter names in the method;
  • Line 591 __V(_, ___(u_), 0, 0, _U__).apply(void 0, y__(v___))is the result of the execution of the current method.

Some people must be wondering, how do you know that log breakpoints should be set in these three places? The answer is that you can only debug in one step and multi-step by yourself, find the rules, and observe carefully. Of course, it is not only these three places that can output the corresponding information, there may be other places, it depends on your own debugging.

12

13

14

In addition to the log breakpoints in these three places, it is recommended to make a breakpoint on line 606, so that each time a method is executed, the breakpoint is interrupted, and the local can follow the synchronous debugging, and compare the incoming parameters and the obtained results one by one. , so as not to output too many things at once, it is inconvenient to find.

The first step, gnature = window.byted_acrawler(window.sign()):

15

The next step, the sign method, takes the timestamp:

16

Next, call the hex_md5()method :

17

From here, you can start local synchronous debugging. When you debug the breakpoint under local MD5, you can see that the obtained values ​​are different:

18

Next, call the str2binl()method , and get the same value locally:

19

20

Next, call the core_md5()method , and the value obtained is different from the local one. Here, it can be roughly determined that there is a difference between this method and the standard algorithm:

21

22

Next, call the md5_ff()method and get the same value:

23

24

By analogy, two different places will eventually be found, that is, the md5_hh()two default parameters have been modified.

-722521979was changed to -722521939:

25

26

76029189was changed to 76029185:

27

28

The final submission result, the verification is successful:

29

{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4585873/blog/5502977