Chapter Three:实战 js 混淆 - 源码乱码(简单)

Posted Amo Xiang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Chapter Three:实战 js 混淆 - 源码乱码(简单)相关的知识,希望对你有一定的参考价值。

目录

1.第一题: js 混淆 - 源码乱码(简单)

1.1 前置知识

https://blog.csdn.net/xw1680/article/details/124605393?spm=1001.2014.3001.5502

1.2 猿人学Web端爬虫攻防刷题平台-第一题

1.2.1 简单分析

链接网址:https://match.yuanrenxue.com/match/1 分析过程如下:

  1. 打开开发者工具,就发现它立即进入了断点模式,如下图所示:
    经过分析这是一个无限 debugger,我们采取禁用部分断点的方式解决它,方便直接,在第2行行号处,单击鼠标右键,选择 Never pause here,然后点击一下 Resume script execution(恢复脚本执行)按钮,跳过当前断点,页面就不会再进入到无限 debugger 的状态了。如果对此操作不太熟悉的,可以参照上文。
  2. 分析接口。优先考虑导航栏的地址,如下图所示:

    发现页面中的数据并没有在该接口中,所以此时考虑页面中的数据是通过Ajax获取的,我们切换到 Fetch/XHR 选项:

    故我们切换到 Headers 选项卡查看该接口如下:
    https://match.yuanrenxue.com/api/match/1?m=aacf82d0f329c9c1e59dfa36707ea5e7%E4%B8%A81650606204
    
    发现是一个 GET 请求,并且URL中有一个m参数,我们点击第2页和第3页的按钮,观察下:
    https://match.yuanrenxue.com/api/match/1?page=2&m=9800f2aa367afb6d2ce9d1e072765fa7%E4%B8%A81650606849
    https://match.yuanrenxue.com/api/match/1?page=3&m=f9d5e45207628770382bfaf11910fdd9%E4%B8%A81650606851
    
    分析几个接口发现 page 代表哪一页的数据,第一页为1,第二页为2,第三页为3,以此类推,这个比较好分析,但是我们也可以看到参数 m 是在不停变化的,并且没有什么特别明显的规律,我们要构造请求就必须分析出 m 是怎么构造而来的,此时需要js逆向。

1.2.2 js逆向过程分析

在上面我们分析出了获取数据的接口,但是其中的m参数怎么构造的呢?

可以采取全局搜索的方式,但是一个m出现的地方肯定很多,大胆猜测全局搜索的效果并不是特别理想,我们可以加上一些标志符号,如m=,如下图所示:

但是可以看到效果也不是特别理想,并不能清晰的看出m生成的逻辑(下方还有很多,我这里省略掉了)那怎么办呢?我们刚刚分析了数据是通过Ajax获取的,那我们可以采取 Ajax 断点的分析方法,然后观察Call Stack,通过它可以顺着找到前序调用逻辑,顺着它一层一层找,看是否可以找到构造 Ajax 请求的逻辑。每个接口都包含:https://match.yuanrenxue.com/api/match/1?page= 故我们可以向下图所示的方法添加Ajax断点:

这时候我们再点击翻页按钮2,触发第2页的 Ajax 请求,会发现点击之后页面走到断点停下来了,如下图所示:

格式化代码看一下,发现它停到了 Ajax 最后发送的那个时候,即底层的 XMLHttpRequest 的 send 方法,可是似乎还是找不到 Ajax 请求是怎么构造的,前面我们说过 Call Stack,通过它可以顺着找到前序调用逻辑,所以顺着它一层一层找,也可以找到构造 Ajax 请求的逻辑,最后会找到一个叫作 request(请求) 的方法,如下图所示:

这里我们看到了 m 参数。接下来我们将 Ajax 断点去掉,在第6行添加断点,再点击翻页按钮3,看是否会触发断点:

发现确实断住了,说明我们找到的位置是正确的,只是这里的代码被混淆了,我们不便于观看。我们看到右侧的 Local 域中有4个变量,其中 _0xb89747 对象中的 m 属性就是我们 get 请求中的 m 参数,接下来我们分析 _0xb89747 是怎么来的,一步步还原就可以了。

我们把上面的代码复制出来:

var _0x2268f9 = Date['\\x70\\x61\\x72\\x73\\x65'](new Date()) + (16798545 + -72936737 + 156138192), _0x57feae = oo0O0(_0x2268f9['\\x74\\x6f\\x53\\x74\\x72' + '\\x69\\x6e\\x67']()) + window['\\x66'];
const _0x5d83a3 = ;
_0x5d83a3['\\x70\\x61\\x67\\x65'] = window['\\x70\\x61\\x67\\x65'], _0x5d83a3['\\x6d'] = _0x57feae + '\\u4e28' + _0x2268f9 / (-1 * 3483 + -9059 + 13542);
var _0xb89747 = _0x5d83a3;

观察代码发现 _0xb89747 其实就是 _0x5d83a3_0x5d83a3 就是下面这个代码:

_0x5d83a3['\\x70\\x61\\x67\\x65'] = window['\\x70\\x61\\x67\\x65'], _0x5d83a3['\\x6d'] = _0x57feae + '\\u4e28' + _0x2268f9 / (-1 * 3483 + -9059 + 13542);

我们将其进行简化,借助Console面板:

var _0x5d83a3['page'] = window['page']
// \\u4e28 其实是一个丨 12b28427a219ee4dd04a57e8e7b07a6b丨1650618899 丨
var _0x5d83a3['m'] = _0x57feae + '\\u4e28' + _0x2268f9 / 1000

从之前的代码我们可以看到:

var _0x2268f9 = Date['\\x70\\x61\\x72\\x73\\x65'](new Date()) + (16798545 + -72936737 + 156138192)
_0x57feae = oo0O0(_0x2268f9['\\x74\\x6f\\x53\\x74\\x72' + '\\x69\\x6e\\x67']()) + window['\\x66'];
// 简化:
// 1.其实这里就是调用 Date.parse()方法接收一个当前时间的字符串参数,并将这个字符串转换为表示该日期的毫秒数,再加上100000000
var _0x2268f9 = Date['parse'](new Date()) + 100000000
// 2.将_0x2268f9转换为字符串类型的数据,在丢入oo0O0方法中处理后,拼接上window['f']的值
var _0x57feae = oo0O0(_0x2268f9['toString']()) + window['f']

那这里我们将 _0x2268f9 的逻辑找到了,只要分析出 _0x57feae,我们就能构造 _0x5d83a3[‘m’] 了,接下来我们重点看一下下面这个语句:

var _0x57feae = oo0O0(_0x2268f9['toString']()) + window['f']
// window['f'] 我们不知道是啥 借助控制台输出一下 发现是 '54d105d76aff95e7bd4e0f72de91f4f9'
// 刚好是 _0x57feae 的值,但是具体  window['f'] 的值是怎么构造的,我们需要去分析逻辑,搜索一下
// 发现没有 window['f'] 这个东西,那我们只能试着从跟它一行 oo0O0() 函数去着手分析了

通过调试断点,找到函数oo0O0的位置,如下图所示:


将整行代码复制到 IDE 工具中,格式化如下:

首先在控制台打印出这个语句的输出结果:

eval(atob(window['b'])[J('0x0', ']dQW')](J('0x1', 'GTu!'), '\\x27' + mw + '\\x27')); // 发现这个正是window['f']的值

逐行语句调试:atob(window[‘b’]) 返回的是:

[J('0x0', ']dQW')] ==> 'replace'
(J('0x1', 'GTu!'), '\\x27' + mw + '\\x27') ==> ('mwqqppz', '')
// 这里的mv 我重新取一个值: 1650610157000
(J('0x1', 'GTu!'), '\\x27' + 1650610157000 + '\\x27') ==> ('mwqqppz', "'1650610157000'")

将上面的代码改写:

eval(atob(window['b'])[J('0x0', ']dQW')](J('0x1', 'GTu!'), '\\x27' + mw + '\\x27'));
eval(atob(window['b'])['replace']('mwqqppz', "'1650610157000'"));
//atob: 方法用于解码一个 base-64 编码的字符串
// 这句话的意思就是一个字符串调用replace()方法将其中的子串mwqqppz替换为'1650610157000'
// eval() 函数计算 javascript 字符串,并把它作为脚本代码来执行 故:
// 替换后的字符串被放到eavl()函数中当作表达式来执行,会调用hex_md5函数对当前时间戳进行加密
window.f = hex_md5('1650610157000') 
// 故我们就分析出了 m参数是怎么来的了 其实就是md5加密 如果你对js算法比较熟悉的话,一开始就可以进行尝试的

1.2.3 代码实现

构造m参数相关的js代码:

function getM()
    var hexcase = 0;
    var b64pad = "";
    var chrsz = 16;
    function hex_md5(a) 
        return binl2hex(core_md5(str2binl(a), a.length * chrsz))
    
    var _0x2268f9 = Date['\\x70\\x61\\x72\\x73\\x65'](new Date()) + (16798545 + -72936737 + 156138192)
    function core_md5(p, k) 
        p[k >> 5] |= 128 << ((k) % 32);
        p[(((k + 64) >>> 9) << 4) + 14] = k;
        var o = 1732584193;
        var n = -271733879;
        var m = -1732584194;
        var l = 271733878;
        for (var g = 0; g < p.length; g += 16) 
            var j = o;
            var h = n;
            var f = m;
            var e = l;
            o = md5_ff(o, n, m, l, p[g + 0], 7, -680976936);
            l = md5_ff(l, o, n, m, p[g + 1], 12, -389564586);
            m = md5_ff(m, l, o, n, p[g + 2], 17, 606105819);
            n = md5_ff(n, m, l, o, p[g + 3], 22, -1044525330);
            o = md5_ff(o, n, m, l, p[g + 4], 7, -176418897);
            l = md5_ff(l, o, n, m, p[g + 5], 12, 1200080426);
            m = md5_ff(m, l, o, n, p[g + 6], 17, -1473231341);
            n = md5_ff(n, m, l, o, p[g + 7], 22, -45705983);
            o = md5_ff(o, n, m, l, p[g + 8], 7, 1770035416);
            l = md5_ff(l, o, n, m, p[g + 9], 12, -1958414417);
            m = md5_ff(m, l, o, n, p[g + 10], 17, -42063);
            n = md5_ff(n, m, l, o, p[g + 11], 22, -1990404162);
            o = md5_ff(o, n, m, l, p[g + 12], 7, 1804660682);
            l = md5_ff(l, o, n, m, p[g + 13], 12, -40341101);
            m = md5_ff(m, l, o, n, p[g + 14], 17, -1502002290);
            n = md5_ff(n, m, l, o, p[g + 15], 22, 1236535329);
            o = md5_gg(o, n, m, l, p[g + 1], 5, -165796510);
            l = md5_gg(l, o, n, m, p[g + 6], 9, -1069501632);
            m = md5_gg(m, l, o, n, p[g + 11], 14, 643717713);
            n = md5_gg(n, m, l, o, p[g + 0], 20, -373897302);
            o = md5_gg(o, n, m, l, p[g + 5], 5, -701558691);
            l = md5_gg(l, o, n, m, p[g + 10], 9, 38016083);
            m = md5_gg(m, l, o, n, p[g + 15], 14, -660478335);
            n = md5_gg(n, m, l, o, p[g + 4], 20, -405537848);
            o = md5_gg(o, n, m, l, p[g + 9], 5, 568446438);
            l = md5_gg(l, o, n, m, p[g + 14], 9, -1019803690);
            m = md5_gg(m, l, o, n, p[g + 3], 14, -187363961);
            n = md5_gg(n, m, l, o, p[g + 8], 20, 1163531501);
            o = md5_gg(o, n, m, l, p[g + 13], 5, -1444681467);
            l = md5_gg(l, o, n, m, p[g + 2], 9, -51403784);
            m = md5_gg(m, l, o, n, p[g + 7], 14, 1735328473);
            n = md5_gg(n, m, l, o, p[g + 12], 20, -1921207734);
            o = md5_hh(o, n, m, l, p[g + 5], 4, -378558);
            l = md5_hh(l, o, n, m, p[g + 8], 11, -2022574463);
            m = md5_hh(m, l, o, n, p[g + 11], 16, 1839030562);
            n = md5_hh(n, m, l, o, p[g + 14], 23, -35309556);
            o = md5_hh(o, n, m, l, p[g + 1], 4, -1530992060);
            l = md5_hh(l, o, n, m, p[g + 4], 11, 1272893353);
            m = md5_hh(m, l, o, n, p[g + 7], 16, -155497632);
            n = md5_hh(n, m, l, o, p[g + 10], 23, -1094730640);
            o = md5_hh(o, n, m, l, p[g + 13], 4, 681279174);
            l = md5_hh(l, o, n, m, p[g + 0], 11, -358537222);
            m = md5_hh(m, l, o, n, p[g + 3], 16, -722881979);
            n = md5_hh(n, m, l, o, p[g + 6], 23, 76029189);
            o = md5_hh(o, n, m, l, p[g + 9],以上是关于Chapter Three:实战 js 混淆 - 源码乱码(简单)的主要内容,如果未能解决你的问题,请参考以下文章

Chapter 1:Create You First 3D Scene With Three.js

WebGL初探—Three.js全景图实战

十分钟快速实战Three.js

Chapter Five:实战入门级js(0难度)

three.js?????????????????????????????????

使用shader着色器程序创建扩散光圈效果(three.js实战10)