1. 程式人生 > 實用技巧 >唯品會密碼JS解密與python模擬登入!

唯品會密碼JS解密與python模擬登入!

上班都快一週了,一直想更新,奈何小夥還沒有從假期的快樂中緩過來,今天終於耐下心來更新一小篇。摳一下某品會的JS程式碼,接著使用摳取的JS程式碼加密密碼進行登入。友情提示:為避免不必要的糾紛,本文中所有網址都進行了一定隱藏。

目標

標題已經闡明瞭本文的目的:

  1. 密碼加密解密;
  2. 利用第一步加密方法加密後進行登入;
  3. 完整程式碼中嘗試了多賬號號批量登入(由於篇幅有限,移動到了閱讀原文中);

摳出程式碼

找到目標網站,進入登入頁面(對各位來說都很easy):

像以往一樣輸入錯誤的賬號密碼,看看提交的資料,點選登入後,提交賬號密碼url如下:

繼續看FormData,如下圖:

圖中的loginName為你輸入的使用者名稱,password為密碼,可以看出被加密了,如果你熟悉了各類JS加密方式,心裡應該能大概確定這就是MD5加密結果。暫時不用管其它的四個引數,接下來無非就是把加密JS程式碼摳出來,可以通過呼叫棧進去,也可以全域性搜尋password來定位JS檔案,具體使用哪種方法就看各位看官的喜好了,我這裡是從呼叫棧進去的(既然我們是在登入,那就點帶有login字樣的js檔案進去好了):

進來後你可以搜一下password關鍵字,應該馬上就能定位到想要的位置,定位的時候別忘了掛上一個斷點,就像下圖一樣:

一看加密方法名就是熟悉的MD5,接下來的操作應該更加熟悉了,啟用斷點(輸入賬號密碼,點選登入,前提是你掛上了斷點)。

點選後就進入了加密方法裡:

function md5(string, key, raw) {
                if (!key) {
                    if (!raw)
                        return hex_md5(string);
                    return raw_md5(string)
                }
                if (!raw)
                    return hex_hmac_md5(key, string);
                return raw_hmac_md5(key, string)
            }

可以看出md5方法一共有三個引數,第一個striing即為密碼,key和raw是undefined。接下笨一點的辦法就是一步一步執行,把跳轉的所有程式碼都扣下來,如果是剛開始學習摳程式碼,這個方法能增加很多除錯”感覺”,慢慢摳得越來越順手。取巧一點的無非就是找到大括號:

一直往上找到,應該馬上就能找到正括號:

這樣裡面的程式碼其實就是本次要摳的程式碼,但是要使用python呼叫,還要進行一點改動。本次要摳的加密程式碼其實很簡單,畢竟只是md5。經過稍微改寫後,我們嘗試使用python裡execjs庫執行:

成功執行出結果,摳取完畢。

登入

FormData

通過上一節,我們已經可以得到加密後的密碼,既然要登入,那就要回到FormData中,看看幾個引數的含義與取值,經過一番測試,總結如下,感興趣的也可以自己去試一下。

引數取值loginName使用者名稱password加密後的密碼remUser是否記住使用者名稱(0或者1)whereFrom可為空captchaId驗證碼(可為空)captchaTicket可為空

Cookies

現在的登入都需要攜帶cookies,看一下本站登入時候的Cookies:

看起來很多,但是經過測試並不需要全部攜帶,cookies應該是服務端產生的,所以還是老老實實帶上吧,沒有捷徑。經過粗略測試,攜帶以下幾個即可登入:

cookies = {
            ' mars_pid': '你的',
            ' cps': '你的',
            ' mars_sid': '你的',
            'times_XXXXX': '你的',
            ' VipRUID': '你的',
            ' VipRNAME': '',
            ' VipDegree': '你的',
            ' user_class': '你的',
            ' VipCI_te': '你的',
            'mars_cid': '你的'
        }

有興趣的可以繼續試試這裡面還有沒有可以不用攜帶的。

Header

不管是登入還是爬取某個網頁,請求頭是絕對繞不過的,現在一起看看本站的請求頭都有哪些:

好傢伙,各種都有,不過大家不用擔心,經過測試在本文實際爬取過程中只用攜帶user-agent,平時的爬取過程中,有些可能會檢查上一個網頁是在哪裡,即要攜帶referer,還有origin和host也是可能會攜帶的。

正式登入

本次登入的幾個要點在前面應該算已經闡述清楚了,現在要做的無非就是把FormData裡的資料post到login_url。

flowchat
st=>start: 開始
e=>end: 結束
op0=>operation: 輸入賬號密碼
op1=>operation: 執行js程式碼加密密碼
op=>operation: 組裝formdata
op2=>operation: 把formdata post到登入url
op3=>operation: 分析返回

st->op0->op1->op->op2->op3->e

好了,流程知道了開始寫程式碼,友情提示:由於可能引起一些不必要的紛爭,程式碼中的url和cookies值都隱藏了,大家按照自己的新增即可。建立了一個VipLogin類,下面是部分模組的程式碼。

執行js程式碼

@property
    def exec_js(self):
        """
        你自己的js路徑
        :return:
        """
        with open('..//js//weipinhui.js', encoding='utf-8') as f:
            weipinhui = f.read()
        js = execjs.compile(weipinhui)
        return js

加密密碼

def get_pwd(self,password):
        key = ""
        raw = ""
        pwd = self.js.call('md5', password, key, raw)
        logger.info("加密結果為:{}".format(pwd))
        return pwd

使用者名稱加密

def encrypt_phone(self,phone):
        return phone[:3]+"*"*5+phone[-3:]

提交資料

def login(self,user,passwd):
        if not user.isdigit or len(user) != 11:
            logger.error("目前僅支援手機號方式登入:{}".format(user))
            return False
        session = requests.Session()
        vip_name = self.encrypt_phone(user)
        times_key = "times_"+user
        data = {
            "loginName": user,
            "password": passwd,
            "remUser": "0",
            "whereFrom":"",
            "captchaId":"" ,
            "captchaTicket":"",
        }
        ck_dict = {
            ' mars_pid': '你的',
            ' cps': '你的',
            ' mars_sid': '你的',
            times_key: '你的',
            ' VipRUID': '你的',
            ' VipRNAME': vip_name,
            ' VipDegree': '你的',
            ' user_class': '你的',
            ' VipCI_te': '你的',
            'mars_cid': '你的'
        }
        requests.utils.add_dict_to_cookiejar(session.cookies,ck_dict)
        resp = session.post(self.login_url,headers=self.headers,data=data,verify=False)
        ret = json.loads(resp.text)
        logger.info("賬號{}返回結果為:{}".format(self._encrypt_phone(user),ret))
        if ret.get("result","") == "success":
            return True
        return False

登入結果

登入成功的標誌為'result': 'success‘如下:

{
    'result': 'success', 
    'errorCode': 0,
     'data': {
         'redirectUrl': 'https://www.xxx.com', 
         'captchaFlowData': None, 
         'extend': None,                  
         'bindMobile': True, 
         'illegalState': False
         }, 
     'redirectUrl': 'https://www.xxx.com'
 }

完整專案程式碼獲取點這裡