博客 / 詳情

返回

【驗證碼逆向專欄】房某下登錄滑塊逆向分析

聲明

本文章中所有內容僅供學習交流使用,不用於其他任何目的,不提供完整代碼,抓包內容、敏感網址、數據接口等均已做脱敏處理,嚴禁用於商業用途和非法用途,否則由此產生的一切後果均與作者無關!

本文章未經許可禁止轉載,禁止任何修改後二次傳播,擅自使用本文講解的技術而導致的任何意外,作者均不負責,若有侵權,請在公眾號【K哥爬蟲】聯繫作者立即刪除!

前言

K 哥之前在【JS 逆向百例】專欄中寫過一篇文章:【JS 逆向百例】房某下登錄接口參數逆向,該站如果通過輸入賬號和密碼的方式進行登錄,POST 請求參數中,密碼 pwd 被加密處理了,對其進行了逆向分析。最近在某博客平台上,有粉絲在該篇文章的評論區詢問能不能出一期該站的滑塊逆向文章,經過研究發現通過手機動態碼的方式登錄,點擊獲取短信驗證碼時,會彈出滑塊驗證,本文將對另一種登錄方式的反爬策略進行研究分析,既是滿足粉絲需求,也是對該站登錄逆向的補充完善。

02

逆向目標

  • 目標:房某下手機動態碼登錄,滑塊驗證碼逆向分析
  • 網站:aHR0cHM6Ly9wYXNzcG9ydC5mYW5nLmNvbS8=

03

抓包分析

隨便輸入一串手機號碼,點擊獲取短信驗證碼,即會彈出滑塊驗證,getslidecodeinit.api 接口響應返回 challengegt 參數的值,這兩個參數在後面校驗滑塊驗證和獲取短信驗證碼的時候會用到:

04

c=index&a=jigsaw 接口響應返回的參數中,surl 為滑塊驗證碼的背景圖片,url 為滑塊圖片,完整的下載地址需要在前面加上 https://static.soufunimg.com/common_m/m_recaptcha/jigsawimg/

05

需要注意的是,下載下來的背景圖片(320x160)以及滑塊圖片(60x158)的長寬與網頁上渲染出來的是不一致的:

06

渲染出來的背景圖片為 300x150,滑塊為 57x150,需要先對獲取到的圖片進行縮放處理後,再識別缺口距離:

07

拖動滑塊進行驗證,c=index&a=codeDrag 接口響應返回校驗的結果,請求參數中 it 經過了加密處理,需要逆向還原出加密算法,後文會進行研究分析,callback 生成方式如下:

"fangcheck_" + (parseInt(1e4 * Math.random()) + (new Date).valueOf())
  • 1e4 * Math.random():生成一個介於 0 到 10000 之間的隨機數;
  • (new Date).valueOf():獲取當前的時間戳(以毫秒為單位)。

challengegt 參數是前面所説的 getslidecodeinit.api 接口響應返回,startend 為滑動軌跡開始及結束的時間戳:

08

滑塊驗證失敗,code 有兩種狀態碼:

101 ---> 參數校驗失敗

101

102 ---> 缺口識別錯誤

09

滑塊驗證成功,code 為 100:

10

驗證成功之後,會響應返回 validate 參數,攜帶該參數請求 loginsendmsm.api 接口,即可成功發送短信驗證碼:

11

發送成功,響應返回的 message 為 Success,失敗則為 Error:

12

逆向分析

i 參數

先來分析下 i 參數是如何加密生成的,從驗證接口跟棧到 jigsawpc.1.0.1.js 文件中:

13

ctrl + f 搜索 i:,只有一個結果:

14

在第 204 行打下斷點,滑動滑塊即會斷住,可以看到,l 即滑動軌跡,由 x 軸、y 軸距離以及時間戳組成,後面再對軌跡進行分析,前文所講到的 startend 在此驗證了,為滑動的開始及結束時間:

15

從第 203 行,跟進到 x.compress 方法中去:

16

可以看到,i 參數的值就是由 x.baseCompress 方法生成的,傳入的 e 參數很像是由一些值拼接而成的:

17

回到第 203 行,e 參數是由 function(e) {...} 方法生成的,點擊前大括號,找到該函數結束的位置,在第 301 行打下斷點,斷住後會發現,e 參數的值是先通過 join( ) 方法將 r 數組的所有元素用 !! 符分隔後連接成一個字符串,再使用 encodeURIComponent( ) 方法進行編碼後得到的:

18

r 數組是由哪些元素組成的呢?往上跟到第 296 行就會發現,r 數組中的元素如下,包括一些瀏覽器環境,最後確實校驗了,但不多:

["textLength", "HTMLLength", "documentMode", "screenLeft", "screenTop", "screenAvailLeft", "screenAvailTop", "innerWidth", "innerHeight", "outerWidth", "outerHeight", "browserLanguage", "browserLanguages", "systemLanguage", "devicePixelRatio", "colorDepth", "userAgent", "cookieEnabled", "netEnabled", "screenWidth", "screenHeight", "screenAvailWidth", "screenAvailHeight", "localStorageEnabled", "sessionStorageEnabled", "indexedDBEnabled", "CPUClass", "platform", "doNotTrack", "timezone", "canvas2DFP", "canvas3DFP", "plugins", "maxTouchPoints", "flashEnabled", "javaEnabled", "hardwareConcurrency", "jsFonts", "timestamp", "performanceTiming", "cwidth"]

下面是對數組中各環境屬性的簡單描述,可供參考:

  • textLength:用於測量 HTML 元素文本內容的長度;
  • HTMLLength:獲取當前文檔中 HTML 根元素的內部 HTML 內容的長度;
  • documentMode:用於在 Internet Explorer 瀏覽器中確定文檔的呈現模式;
  • screenLeftscreenTop:窗口左上角相對於屏幕左上角的座標;
  • screenAvailLeftscreenAvailTop:可用屏幕空間左上角相對於屏幕左上角的座標;
  • innerWidthinnerHeight:瀏覽器窗口的內部寬度和高度,不包括瀏覽器工具欄和滾動條;
  • outerWidthouterHeight:瀏覽器窗口的外部寬度和高度,包括瀏覽器邊框和工具欄;
  • browserLanguagebrowserLanguages:瀏覽器當前使用的語言或語言列表;
  • systemLanguage:操作系統的默認語言;
  • devicePixelRatio:設備像素比,用於在不同分辨率屏幕上進行適配;
  • colorDepth:屏幕顏色深度;
  • userAgent:瀏覽器的用户代理字符串,通常包含瀏覽器和操作系統信息;
  • cookieEnabled:表示瀏覽器是否啓用了 Cookie;
  • screenWidthscreenHeight:屏幕的寬度和高度;
  • screenAvailWidthscreenAvailHeight:可用屏幕的寬度和高度;
  • localStorageEnabledsessionStorageEnabled:表示瀏覽器是否啓用了本地存儲和會話存儲;
  • indexedDBEnabled:表示瀏覽器是否啓用了 IndexedDB;
  • CPUClass:表示 CPU 的等級或類別;
  • platform:操作系統平台信息;
  • doNotTrack:表示用户是否啓用了 "不跟蹤" 功能;
  • timezone:用户所在時區;
  • canvas2DFPcanvas3DFP:Canvas 防指紋技術,用於保護用户隱私;
  • plugins:瀏覽器安裝的插件列表;
  • maxTouchPoints:設備支持的最大觸摸點數;
  • flashEnabled:表示瀏覽器中是否啓用了 Flash;
  • javaEnabled:表示瀏覽器中是否啓用了 Java 插件;
  • hardwareConcurrency:表示設備的邏輯處理器核心數;
  • jsFonts:瀏覽器已安裝的字體列表;
  • timestamp:時間戳,通常用於測量性能和時間間隔;
  • performanceTiming:訪問有關頁面加載和性能計時的信息。

至此 e 參數的構成方法分析完了,再回到 x.compress 方法中,也就是第 505 行,前文分析了,i 參數由 x.baseCompress 方法生成,該方法傳入了三個參數,前兩個已經分析完了,來看看第三個函數部分:

function(e) {
    return x.toChart16(t(e))
}

t 方法定義在第 502 行,就是 String.fromCharCode( ),它用於將一組 Unicode 值(UTF-16 編碼)轉換成對應的字符串,每個參數都是一個表示 Unicode 值的整數。再跟進到 x.toChart16 方法中去,定義在第 628 行,直接扣下來就行了:

19

最後直接將 baseCompress 方法扣下來即可,i 參數就分析完了:

20

t 參數

生成 t 參數的方法定義在第 302 行,同樣搜 t: 就可以找到,和 i 一樣,也是幾個自執行函數,直接跟到第 392 行,打下斷點,斷住後驗證了,t 參數就是在這裏生成的:

21

t 參數是於一長串二進制字符串 e 中從前往後依次截取六位字符,再通過 parseInt 方法將截取到的二進制字符串轉換為整數,即索引,最後使用 charAt 方法根據索引從固定字符串 E 中取值,循環 e.length / 6 次後拼接而成的:

22

那一長串二進制字符串怎麼來的呢?生成 t 參數的函數是個自執行函數,傳入的參數是 ll 定義在第 368 行,生成方法逐個跟,扣下來即可:

23

接着往上跟到 return 處,即第 360 行,此時傳入的 e 為鼠標軌跡,很明顯,這裏對軌跡做了處理,不再是前文所講的 x、y、t 形式,被轉換成了一個大數組:

24

相關轉換算法在第 180 行,即 e 參數,軌跡校驗的不是很嚴格,模擬構造即可:

25

結果驗證

26

user avatar bell_lemon 頭像 yongle_hengdi 頭像 serein_6100cd5f80ea0 頭像
3 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.