富有的,给他更多;没有的,把他仅有的也拿走。
爬虫真是一个有意思的东西,简单的页面,不费力就能爬取,复杂的页面,费尽心思也不知如何爬取。就像玩游戏,刚开始时在新手村砍稻草人,等出了新手村,你面对的直接就是最终boss。
最近有点喜欢爬虫js逆向了,喜欢那种被虐的死去活来,悲痛欲绝的感觉(没错说的就是你,只狼)。
最近学习爬虫,发现网上免费的资料教程,要么很简单,在headers中添加user-agent就能爬取,要么很高深,照着读了好几篇也搞不懂到底是怎么实现的,并且还因为某种原因不会放出所有的代码,只能浅尝辄止。
所以说:绝知此事要躬行。
于是打算好好钻研钻研爬虫,多多动手,把过程也记录下来,跟大家分享,我尽可能用最详细的描述,如果你跟着我一起学的话,你也一定能学会噢!
今天要做的是猿人学js逆向
的web第一题:抓取机票的价格。准备好小板凳哦,本文较长。
此题标注为简单题,主要思路是寻找实现加密字符串的代码,提取并复现。
准备工作
Cookie AutoDelete
插件,便于清除cookie (关注本公众号(程序员小木屋)回复"cookie"可获取下载地址) https://match.yuanrenxue.com/match/1#
,并打开开发者工具。 首先要解决的是无限debugger
问题,上述代码的意思是,有一个每隔500毫秒就运行一次的函数,函数的内容就是在此处暂停,我们需要解决的是,不让函数在此处暂停。
网络
tab栏查看所有接口,找到传输机票价格的接口 经过查看,headers中没有异常参数,但是传参中有一个参数m
,我们需要找到这个m
是如何生成的。
技巧讲解
m
参数的位置 技巧讲解
m
是如何生成的 技巧讲解
_0x5d83a3['\x70\x61\x67\x65'] = window['\x70\x61\x67\x65'], _0x5d83a3['\x6d'] = _0x57feae + '\u4e28' + _0x2268f9 / (-1 * 3483 + -9059 + 13542);
例如,第5行的代码,如上,我们依次在控制台中输入这些变量,查看是什么含义
看到了没有,_0x5d83a3['\x6d']
就是m
,_0x57feae
是m
的前半部分,_0x2268f9 * 1000
就是m
的后半部分(即时间戳)。
m
的后半部分,继续查找前半部分,看第2行,前半部分是在这里生成的 发现_0x2268f9
是时间戳,oo0O0(_0x2268f9['\x74\x6f\x53\x74\x72' + '\x69\x6e\x67']())
是一个暂时不知道干啥的函数,window['\x66']
正确输出了m
的前半段。
window['\x66']
'\x66'
即'f'
,我们要找到window.f
这个变量。 但是根据我的ctrl+f
查找,并没有找到window.f
,这是我们大胆假设,难道这个变量是在前面那一段不知道干啥用的函数生成的?我们进入oo0O0
函数中
代码跳转到了这里,oo0O0
函数就隐藏在其中,这可怎么办呢?
不要慌!我们试着把它解混淆和格式化。
首先,解混淆是针对js代码的,这行代码里混合着,我们把这行代码复制到pycharm中,查找其中的非js代码,把它删掉
删掉后,现在就只剩js代码了。
百度搜索"js解混淆在线",进入到一个解混淆网址 解混淆 把代码复制进去,点击js格式化,就能看到规范的oo0O0
代码了!
注意!
如果你的代码没有正确格式化,说明你没有正确删除,或语法格式有问题(比如落了;
分号)。 这里需要一点js基础哈
正确的oo0O0
如下
function oo0O0(mw) {
window.b = '';
for (var i = 0,
len = window.a.length; i < len; i++) {
console.log(window.a[i]);
window.b += String[document.e + document.g](window.a[i][document.f + document.h]() - i - window.c)
}
var U = ['W5r5W6VdIHZcT8kU', 'WQ8CWRaxWQirAW=='];
var J = function(o, E) {
o = o - 0x0;
var N = U[o];
if (J['bSSGte'] === undefined) {
var Y = function(w) {
var m = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=',
T = String(w)['replace'](/=+$/, '');
var A = '';
for (var C = 0x0,
b, W, l = 0x0; W = T['charAt'](l++);~W && (b = C % 0x4 ? b * 0x40 + W: W, C++%0x4) ? A += String['fromCharCode'](0xff & b >> ( - 0x2 * C & 0x6)) : 0x0) {
W = m['indexOf'](W)
}
return A
};
var t = function(w, m) {
var T = [],
A = 0x0,
C,
b = '',
W = '';
w = Y(w);
for (var R = 0x0,
v = w['length']; R < v; R++) {
W += '%' + ('00' + w['charCodeAt'](R)['toString'](0x10))['slice']( - 0x2)
}
w = decodeURIComponent(W);
var l;
for (l = 0x0; l < 0x100; l++) {
T[l] = l
}
for (l = 0x0; l < 0x100; l++) {
A = (A + T[l] + m['charCodeAt'](l % m['length'])) % 0x100,
C = T[l],
T[l] = T[A],
T[A] = C
}
l = 0x0,
A = 0x0;
for (var L = 0x0; L < w['length']; L++) {
l = (l + 0x1) % 0x100,
A = (A + T[l]) % 0x100,
C = T[l],
T[l] = T[A],
T[A] = C,
b += String['fromCharCode'](w['charCodeAt'](L) ^ T[(T[l] + T[A]) % 0x100])
}
return b
};
J['luAabU'] = t,
J['qlVPZg'] = {},
J['bSSGte'] = !![]
}
var H = J['qlVPZg'][o];
return H === undefined ? (J['TUDBIJ'] === undefined && (J['TUDBIJ'] = !![]), N = J['luAabU'](N, E), J['qlVPZg'][o] = N) : N = H,
N
};
eval(atob(window['b'])[J('0x0', ']dQW')](J('0x1', 'GTu!'), '\x27' + mw + '\x27'));
return ''
};
但是呢,观察代码,并没有发现代码中有定义或生成window.f
的地方啊。
说得对!同时细心的你应该也发现了这样一行代码
eval(atob(window['b'])[J('0x0', ']dQW')](J('0x1', 'GTu!'), '\x27' + mw + '\x27'));
接下来又是大胆猜想小心求证
的时间:是不是eval...
这行代码生成的呢?
将其复制到控制台,试着运行一下
J
变量不存在,但是atob(window['b']
输出了一段代码,看到代码里的md5
字样,你的心有没有激动起来呀~
将上面代码进行格式化,然后尝试运行一下
咦报错了?不要慌!这里缺少的mwqqppz
不就是执行oo0O0
函数时传进来的时间戳嘛
重新执行一下 出来了! 跟继续执行的结果比较一下 一摸一样! 11. 太棒了!接下来试验一下
hex_md5.js
文件,把 atob(window['b']
展示的这段代码复制进去 run
函数去执行它(这里又需要一些js基础) run.py
文件,然后输入以下代码,我们先试试能不能运行这个js文件 import execjs
from pathlib import Path
context = execjs.compile(Path("./hex_md5.js").read_text())
[ts, hex_md5] = context.call('run')
print(ts, hex_md5)
依次点击第2345页,发现两个重要的点:
user-agent='yuanrenxue.project'
在run.py
中写入如下代码(不懂的地方看注释哦)
import execjs
from pathlib import Path
import requests
context = execjs.compile(Path("./hex_md5.js").read_text())
# url地址
url = "https://match.yuanrenxue.com/api/match/1"
# 定义headers
headers = {"user-agent": "yuanrenxue.project"}
[ts, hex_md5] = context.call("run")
# 生成m参数
m = f"{hex_md5}丨{ts}"
# 记录总和
res = 0
for page in range(1, 6):
# 生成params
params = {"page": page, "m": m}
# 获取数据
resp = requests.get(url, params=params, headers=headers)
json_data = resp.json()
# {'status': '1', 'state': 'success', 'data': [{'value': 8179}, {'value': 6177},
# {'value': 4174}, {'value': 5945}, {'value': 9556}, {'value': 2318}, {'value': 4},
# {'value': 2653}, {'value': 4855}, {'value': 1370}]}
res += sum(map(lambda x: x["value"], json_data["data"]))
print("总和为", res)
运行结果:
总和为 235000
end
本文由 mdnice 多平台发布