前面幾期摳的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
源碼吧,當然不關注也無所謂,有問題隨時私信。