目标链接:https://www.qimao.com/shuku/a-a-a-a-a-a-a-click-1/
直接请求的话,没有返回网站内容,而是一大串混淆过后的js代码
重新启动一个无痕浏览器,观察一下请求时候链接的跳转、cookies的变化等
自动跳转的第二次请求就正常返回内容了,而且cookies中多了三个参数,其中acw_tc
aliyungf_tc
是第一次请求返回的,这里忽略,只有 acw_sc__v2
是从其他地方生成的,跳转过程中没有引入其他的js文件,所以可以肯定就是第一次请求返回的js代码生成的
话不多说,直接来上手分析代码
起手就是一个大数组,这里凭经验判断,代码是经过了 ob混淆
,即 JavaScript Obfuscator
去 de4js网站做一个简单的还原
这里就是入口了,是调用 reload函数
进行赋值的,把代码放入vscode调试一下,首先找到reload调用的地方
把自执行
和setInterval
的无效代码去掉
直接运行代码,把我的系统卡死了 😇🐶 应该是那里进行了死循环
还是现在浏览器调试一下吧,先打开开发者模式,然后输入网址,自动就断住了,源码里面应该是有做检测
发现很多常量都是用 _0x55f3
函数做一个混淆,先把部分还原了
从上面两张图可以看到,两个函数其实是一样的,只不过经过 _0x55f3
的混淆,不还原的话看不出来,两个比较重要的函数如下:
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
| var get_acw_sc = function (arg1) { String['prototype']['hexXor'] = function (_0x4e08d8) { var _0x5a5d3b = ''; for (var _0xe89588 = 0x0; _0xe89588 < this['length'] && _0xe89588 < _0x4e08d8[ 'length']; _0xe89588 += 0x2) { var _0x401af1 = parseInt(this['slice'](_0xe89588, _0xe89588 + 0x2), 0x10); var _0x105f59 = parseInt(_0x4e08d8['slice'](_0xe89588, _0xe89588 + 0x2), 0x10); var _0x189e2c = (_0x401af1 ^ _0x105f59)['toString'](0x10); if (_0x189e2c['length'] == 0x1) { _0x189e2c = '\x30' + _0x189e2c; } _0x5a5d3b += _0x189e2c; } return _0x5a5d3b; };
String['prototype']['unsbox'] = function () { var _0x4b082b = [0xf, 0x23, 0x1d, 0x18, 0x21, 0x10, 0x1, 0x26, 0xa, 0x9, 0x13, 0x1f, 0x28, 0x1b, 0x16, 0x17, 0x19, 0xd, 0x6, 0xb, 0x27, 0x12, 0x14, 0x8, 0xe, 0x15, 0x20, 0x1a, 0x2, 0x1e, 0x7, 0x4, 0x11, 0x5, 0x3, 0x1c, 0x22, 0x25, 0xc, 0x24]; var _0x4da0dc = []; var _0x12605e = ''; for (var _0x20a7bf = 0x0; _0x20a7bf < this['\x6c\x65\x6e\x67\x74\x68']; _0x20a7bf++) { var _0x385ee3 = this[_0x20a7bf]; for (var _0x217721 = 0x0; _0x217721 < _0x4b082b['length']; _0x217721++) { if (_0x4b082b[_0x217721] == _0x20a7bf + 0x1) { _0x4da0dc[_0x217721] = _0x385ee3; } } } _0x12605e = _0x4da0dc['\x6a\x6f\x69\x6e'](''); return _0x12605e; }; var _0x5e8b26 = '3000176000856006061501533003690027800375'; var tmp = arg1['unsbox'](); result = tmp['hexXor'](_0x5e8b26); return result; }
get_acw_sc('F8AD2846B6C7DF57413E6306A26A83820526280E');
|
那么接下来就很简单了,我们只要把那两个函数里面的逻辑弄清楚就可以了,把代码抠出来拷贝到 chrome 的 snippets 进行调试
0x
开头的是16进制数字,0x23 表示 2*16 + 3 = 35
\x
开头的是 16进制的unicode编码
这里有一个 parseInt
函数,python没有对应的内置函数,我自己封装了一个函数,来实现相同的逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| ''' 下面的函数是 parseInt(s, 10) 的实现,处理的是有 字母 + 数字混合的情况 如果是纯数字的话,直接int(s)jiu'xing 如果是 parseInt(s, 16),需要配合int(s, 16)使用 '''
def parseInt(s): l = [] for w in s: if s.isdigit(): l.append(w) else: break return int("".join(l))
|
还有一个 ['toString'](0x10)
的操作,其实就是 number.toString(16)
的魔改,这里有介绍,用python实现就是:
代码整理
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
| import requests import re
def unsbox(arg1: str): """ 用来打乱原arg1字符的顺序 """ l1 = [15, 35, 29, 24, 33, 16, 1, 38, 10, 9, 19, 31, 40, 27, 22, 23, 25, 13, 6, 11, 39, 18, 20, 8, 14, 21, 32, 26, 2, 30, 7, 4, 17, 5, 3, 28, 34, 37, 12, 36] l2 = l1.copy() for i in range(len(arg1)): k = arg1[i] for j in range(len(l1)): k2 = l1[j] if k2 == i + 1: l2[j] = str(k) return "".join(l2)
def hexXor(tmp): seed = "3000176000856006061501533003690027800375" s = "" min_length = min(len(tmp), len(seed)) for i in range(0, min_length, 2): p1 = int(tmp[i:i+2], 16) p2 = int(seed[i:i+2], 16) q = hex((p1 ^ p2)).lstrip("0x") if len(q) == 1: q = '\x30' + q s += q return s
def main(): session = requests.session() url = 'https://www.qimao.com/shuku/a-a-a-a-a-a-a-click-1/' session.headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36"}
resp1 = session.get(url) arg1 = re.search(r"arg1='(\w+?)'", resp1.text).group(1) arg2 = hexXor(unsbox(arg1)) session.cookies.update({"acw_sc__v2": arg2}) resp2 = session.get(url) print(resp2.content.decode())
if __name__ == "__main__": main()
|
运行结果