前面几期抠的JS代码都比较简单,这期要扣的就相对要难一些了,建议亲自尝试,不过依然有完整JS代码,获取完整代码方式见文末。
前言
某天看到有外卖员三问王兴,你敢信,那天我居然兴趣突发,想看看美团加密方式是什么,随即打开美团酒店传送门在这:
调试
挠头调试
按下F12
顺便刷新一下页面,看一看都发了什么请求,呼呼啦啦的一堆请求,点击一下XHR
只看ajax
请求(毕竟这些大公司都是前后端分离,数据均靠ajax
方式传输),倒是少了不少请求,这时候发现有大量的service
开头的请求,点进去看一看,发现每一个请求里有一个酒店的信息,如下图:
哈哈,那接下来套路我们不都知道了,
先去请求url
里看看都有什么参数:
看起来也就一个X-FOR-WITH
需要抠一下代码,还不简单,扔到Sources
里找一找,那不得百分百找到???
啊?纳尼。没有???
这可如何是好
片刻后,突然想到是不是可以从调用栈进去看看:
随便点一个进去,进入到一个VM
后其实也没有头绪,该从何入手呢?这时就想到既然是ajax
请求获得的数据,那我不就可以依靠捕捉ajax
请求设置断点了,这个方法前面没提过,直接看下图设置:
去复制刚才service
请求里的几个关键url词:
这时候继续刷新页面就跳转到下面了:
wc,没思路了,我当时在这里跌跌撞撞了很久,调试了很久才发现了一点端倪,兜兜转转半天发现一切隐藏在apply
函数前面if
语句中的i[r]
:
点开i
看看代码在哪里:
就跳到了hookAjax
里:
既然到这里了,那大致也确定所需要的都在这里了,不过到现在还是没有发现X-WITH-FOR的踪影,别急,开始无穷无尽的调试吧,经过一番令人不适的调试后,还是有点起色了,给大家提个小建议,遇到这种目的虚无缥缈的调试优先点进函数看看,比如 var t = p(w);
,不清理缓存的话不一定每次都能进入这个函数,假如你点进去看看就会发现很熟悉,不用怕,调试状态也能进去:
var p = function(e) {
v(e);
e.cts = (new Date).getTime();
var r = JSON.stringify(e);
var t = window["metaIdx"];
var n = releasex.util.convertStringToBytes(t);
var i = releasex.util.convertStringToBytes(t);
var o = releasex.util.convertStringToBytes(r);
var a = releasex.padding.pkcs7.pad(o);
var u = new releasex.ModeOfOperation.cbc(n,i);
var c = u.encrypt(a);
var f = s(c);
return f
};
插曲(几个参数介绍)
其中e
长这样:
JSON.stringify(e)
后长这样:
"{"ts":1598622575690,"cts":1598625557233,"brVD":[1920,423],"brR":[[1920,1080],[1920,1040],24,24],"aM":"","code":"20200828168048912-4581810-KhhOliZQKTvcGeO"}"
ts
和cts
不用说了,都是时间戳,brVD
和brR
对应下面四个值,都是与屏幕有关的,可以算是固定的,aM
为空:
code
的生成方式如下,算是有变化的,每一次打开页面V
函数里的几个字符串都不一样的,这点需要注意:
继续调试
看见new releasex.ModeOfOperation.cbc(n,i);
和encrypt
是不是有点感觉,cbc
这不就是rsa
加密吗?这里肯定有端倪。一路调试到var c = u.encrypt(a)
:
继续下来到var f = s(c);
看这加密结果与url里的X-WITH-FOR
对比貌似有点像,继续调试,结果一次encodeURIComponent
。一步一步运行,跑到了一个平平无奇的函数里,看起来也做了一些操作:
其实到这里已经有点困了,很想去开一局帝国时代,但是还是忍住了,点点点,突然发现:
功夫不负有心人,还好没打开steam
,这藏得可够深啊老铁。
更明显的看这里:
这不就是我们的X-WITH-FOR
么,ok了。
一些提示
调试过程中我略过了一些不太重要的部分,大家可以看一下e:
{"ts":1598535241135,"cts":1598536559784,"brVD":[1920,530],"brR":[[1920,1080],[1920,1040],24,24],"aM":"","code":"20200827169606326-3603852-eSBufuUUYnDnmatVKF"}
抠出来运行
老规矩、老代码:
import execjs
import requests
import json
##获取X-WITH-FOR
with open('..//js//meituan.js', encoding='utf-8') as f:
meituan = f.read()
js = execjs.compile(meituan)
data ={"ts":1598535241135,"cts":1598536559784,"brVD":[1920,530],"brR":[[1920,1080],[1920,1040],24,24],"aM":"","code":"20200827169606326-3603852-eSBufuUUYnDnmatVKF"}
logid = js.call('gt', data)
print(logid)
url = "https://ihotel.meituan.com/group/v2/poi/detail/service?utm_medium=pc&version_name=999.9&poiId=2386321&X-FOR-WITH={}".format(logid)
headers = {
"Host": "ihotel.meituan.com",
"Origin": "https://hotel.meituan.com",
"Referer": "https://hotel.meituan.com/shanghai/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36",
}
#获取酒店列表
rsp = requests.get(url,headers=headers)
#rsp_json = json.dumps(rsp.text,indent=3,ensure_ascii=False)
print(rsp.text)
运行一下看看:
缺少window
,那还不简单 ,像往常一样直接定义一个window={}
,继续运行:
???????
啥情况,定义成window={}
这样不行?结果一番百度,发现nodejs
有一个库jsdom
,可以模拟浏览器生成window
、document
对象。这样使用:
const jsdom = require("D:\nodejs\node_modules\jsdom");
const {JSDOM } = jsdom; //在jsdom中导出JSDOM对象
const { window } = new JSDOM('<!doctype html><html><body></body></html>'); //导出JSDOM中的window对象
global.window = window; //将window对象设置为nodejs中全局对象;
function aa()
{
console.log(window)
}
aa()
测试运行,生成window
对象:
看来是可以,那就用在我们js代码里生成window
对象,继续运行:
哎呦,不错哟!
结束
调试稍微有些难度的网站基本就要很有耐心了,准备好无限调试的准备,还需要继续加强调试技巧啊。
代码获取方式
写了很多,关注的不多,越来越缺少动力了,后面还有网易云JS逆向和滑块逆向,如果对大家有帮助的话麻烦先关注知识图谱与大数据公众号,点击文末阅读更多、阅读更多、阅读更多获取JS
源码吧,当然不关注也无所谓,有问题随时私信。