JS破解--天X查股權穿透圖


上個月有網友問我一道爬蟲筆試題,是爬取某網站的股權穿透圖

 

 

 當時覺得抓包數據的頭部改變不是很多,cookie里只有兩個值不同,分別是cloud_token和cloud_utm,但是那會忙,沒有幫他具體看一下,今天閑下來看了看。

首先,它是一個aoixs請求,是前台發給后台的請求,因為抓包明顯看到發起兩次請求,分別是option和get。直接去發起GET請求會403

 

 

 接下來找請求的js,是走緩存的一個js,這里對它debug可能會有些卡

 

 

 接下來打斷點調試,獲得兩個參數的值

 

 這里邏輯會有點亂,有大量混亂的換行,還好我重新進行編輯了一下,已經找到該有的信息了,接下來找對它們的生成方法就可以了

function discover_getInvestRoot(id, headers) {
  var _this2 = this;
  return new _promise2.default(function(resolve, reject) {
    _this2.pre_relation_company(id).then(function(res) {
      for (var data = res.data, arr = data.v.split(","), fnStr = "", i = 0; i < arr.length; i++)
        fnStr += String.fromCharCode(arr[i]);
      if (eval(fnStr), window.$SoGou$ = (0,_ms2.default)(id),window.wtf) {
        for (var fxck = window.wtf().split(","), fxckStr = "", i = 0; i < fxck.length; i++)
          fxckStr += window.$SoGou$[fxck[i]];
        var exp = new Date;
        exp.setTime(exp.getTime() + 6e5),
        document.cookie = "cloud_utm=" + fxckStr + ";path=/;domain=.tianyancha.com;expires=" + exp.toGMTString(),
        delete window.wtf
      }
      resolve(_promise2.default.resolve(_this2.getCloud("/cloud-equity-provider/v4/equity/indexnode.json", {id: id}, headers)))
    })
  }) }

首先這里傳入的res為之前請求的name.json的返回值,這里可以直接模擬請求,但是要留意一個CLOUDID的值,這個在之后也需要用到

 

 

 這里先利用返回的data里的v,通過  String.fromCharCode  進行遍歷獲得unicode值對應的字符串,拼接在一起就是fnStr,這也就是我們經常請求后發現這個返回的v總是有固定的數值的原因,因為這串fnStr需要被執行,從而固定的位置必須要能拼接為js的關鍵字。

這里window.wtf()就是fnstr內帶有的wtf函數的返回值,而window.$SoGou$就是通過一個函數計算得到的,傳入要查公司的id

 

 

 

 

這里window.$SoGou$的計算函數,這里我們傳的id固定的話,所返回的值也是一定的。最后返回的是

return r = r.length > 1 ? r[1] : r, n._ff(r)

不要被換行所迷惑,這個網站JS好多惡意換行

還是得講一下這個函數,因為寫代碼時差點被繞進去了,這里傳的e為要查公司的id,但是這里只要e正確,真正使用e的地方是4716行的這個e.charCodeAt(0)。那么之前那個function(e)是如何來的?其實這段JS里有個默認值default,這段邏輯是以,分割默認值default,再遍歷這個數組,取第一位之后的值進行添加入數組,最后得到10個數組,每個長度為37,這個與wtf()的返回值長度對應。也就是說只要我們的默認值default不變,則this._bb就不變,就是這段其實我們可以不進行python代碼復寫,可以直接當作常量進行計算.

var r = e.charCodeAt(0) + "";

這句話意思是取 e的第0個位置字符的 ascii編碼,轉為字符串,也就是我們這里‘2’所對應的字符串編碼為50

return r = r.length > 1 ? r[1] : r, n._ff(r)

這句話意思是 r.length永遠大於1,因為r所對應的ascii編碼只有個位數的那些都是特殊標識符,這些不可能作為id的首個字符,所以這里是恆取r[1]傳入n._ff(r)中

這里我們返回的是"0",["6", "b", "t", "f", "2", "z", "l", "5", "w", "h", "q", "i", "s", "e", "c", "p", "m", "u", "9", "8", "y", "k", "j", "r", "x", "n", "-", "0", "3", "4", "d", "1", "a", "o", "7", "v", "g"]

 

 

 

 

 接下來拿到這個37位的數組之后因為游標問題window.wtf()應該都是37以內,進行遍歷取值,得到window.wtf()長度的cloud_num

 

 這樣兩個參數都找齊了,在控制台輸入document.cookiem,然后可以試着進行請求,記得先發option后發get,在get請求時加上CLOUDID以及option后塞入的aliyungf_tc,這樣就會返回200的數據了

 

 

import json
import re
import time
import requests

# 需要查找的id
id = '3007689648'
s = requests.Session()
# 一個cookie過期時間還未定
headers = {
    "Host": "capi.tianyancha.com",
    "Connection": "keep-alive",
    "Origin": "https://dis.tianyancha.com",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36",
    "Accept": "application/json, text/plain, */*",
    "Referer": "https://dis.tianyancha.com/dis/tree?graphId={}&origin=https%3A%2F%2Fwww.tianyancha.com&mobile=&time=15753515647237b28&full=1".format(
        id),
    "Accept-Encoding": "gzip, deflate, br",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Cookie": "TYCID=d9727140157e11eaab41691f9a51d2ed; undefined=d9727140157e11eaab41691f9a51d2ed; ssuid=8686323904; bannerFlag=undefined; RTYCID=26963f4845324e7f8aaf1c9ca2159104; Hm_lvt_e92c8d65d92d534b0fc290df538b4758=1575344515; aliyungf_tc=AQAAAG/4CAKdyAcAnUEYdNm5vLgCnTNI; CLOUDID=756cffe2-228f-42ef-8e3f-528816e86384; CT_TYCID=20b71739a88447dd98725c23c08efc0f; _ga=GA1.2.199777249.1575344515; _gid=GA1.2.1748072841.1575344515; _gat_gtag_UA_123487620_1=1; Hm_lpvt_e92c8d65d92d534b0fc290df538b4758=1575351565;"
}
# js里有段默認值t.default,這里直接debug拿到默認值轉換的數組
default_list = [
    ["6", "b", "t", "f", "2", "z", "l", "5", "w", "h", "q", "i", "s", "e", "c", "p", "m", "u", "9", "8", "y", "k", "j",
     "r", "x", "n", "-", "0", "3", "4", "d", "1", "a", "o", "7", "v", "g"],
    ["1", "8", "o", "s", "z", "u", "n", "v", "m", "b", "9", "f", "d", "7", "h", "c", "p", "y", "2", "0", "3", "j", "-",
     "i", "l", "k", "t", "q", "4", "6", "r", "a", "w", "5", "e", "x", "g"],
    ["s", "6", "h", "0", "p", "g", "3", "n", "m", "y", "l", "d", "x", "e", "a", "k", "z", "u", "f", "4", "r", "b", "-",
     "7", "o", "c", "i", "8", "v", "2", "1", "9", "q", "w", "t", "j", "5"],
    ["x", "7", "0", "d", "i", "g", "a", "c", "t", "h", "u", "p", "f", "6", "v", "e", "q", "4", "b", "5", "k", "w", "9",
     "s", "-", "j", "l", "y", "3", "o", "n", "z", "m", "2", "1", "r", "8"],
    ["z", "j", "3", "l", "1", "u", "s", "4", "5", "g", "c", "h", "7", "o", "t", "2", "k", "a", "-", "e", "x", "y", "b",
     "n", "8", "i", "6", "q", "p", "0", "d", "r", "v", "m", "w", "f", "9"],
    ["j", "h", "p", "x", "3", "d", "6", "5", "8", "k", "t", "l", "z", "b", "4", "n", "r", "v", "y", "m", "g", "a", "0",
     "1", "c", "9", "-", "2", "7", "q", "e", "w", "u", "s", "f", "o", "i"],
    ["8", "q", "-", "u", "d", "k", "7", "t", "z", "4", "x", "f", "v", "w", "p", "2", "e", "9", "o", "m", "5", "g", "1",
     "j", "i", "n", "6", "3", "r", "l", "b", "h", "y", "c", "a", "s", "0"],
    ["d", "4", "9", "m", "o", "i", "5", "k", "q", "n", "c", "s", "6", "b", "j", "y", "x", "l", "a", "v", "3", "t", "u",
     "h", "-", "r", "z", "2", "0", "7", "g", "p", "8", "f", "1", "w", "e"],
    ["7", "-", "g", "x", "6", "5", "n", "u", "q", "z", "w", "t", "m", "0", "h", "o", "y", "p", "i", "f", "k", "s", "9",
     "l", "r", "1", "2", "v", "4", "e", "8", "c", "b", "a", "d", "j", "3"],
    ["1", "t", "8", "z", "o", "f", "l", "5", "2", "y", "q", "9", "p", "g", "r", "x", "e", "s", "d", "4", "n", "b", "u",
     "a", "m", "c", "h", "j", "3", "v", "i", "0", "-", "w", "7", "k", "6"],
]

def getfnstr(data):
    fnstr = ""
    for i in data.split(','):
        fnstr += chr(int(i))
    return fnstr

def getSogo(default_list, id):
    r = str(ord(id[0]))
    return default_list[int(r[1])]

def getfxckStr(fxck, window_sogo):
    fxckStr = ""
    for i in fxck.split(','):
        fxckStr += window_sogo[int(i)]
    return fxckStr

# 獲取前置參數 random為13位時間戳
res1 = s.get("https://capi.tianyancha.com/cloud-equity-provider/v4/qq/name.json?id={}?random={}".format(id, str(
    int(time.time() * 1000))), headers=headers)
data_dict = json.loads(res1.content)["data"]


# 調用加密函數,獲取cloud_token 以及cloud_utm
fnstr = getfnstr(data_dict.get('v'))
cookie_token = re.search('cookie=\'cloud_token\=(.*?)\;', fnstr).group(1)
wtf_return = re.search('return\'(.*?)\'', fnstr).group(1)
window_sogo = getSogo(default_list, id)
# cloud_utm
fxckStr = getfxckStr(wtf_return, window_sogo)
headers["Cookie"] = headers["Cookie"] + " cloud_utm=" + fxckStr + "; cloud_token=" + cookie_token + ';'
res2 = s.get('https://capi.tianyancha.com/cloud-equity-provider/v4/equity/indexnode.json?id={}'.format(id),
             headers=headers)
text = json.loads(res2.text)
print(text)

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM