【JS逆向百例】東某航空數美指紋 v4 裝置 ID 逆向分析

K哥爬虫發表於2024-12-10

7xrWpJ.png

宣告

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

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

逆向目標

目標:東某航空某數指紋 v4 裝置 ID 逆向分析

網站:aHR0cHM6Ly93d3cuZG9uZ2hhaWFpci5jb20v

抓包分析

開啟網站,找到返回機票資訊的機票查詢介面 flightSearch,分析發現兩個加密引數需要分析:

7AJNrL.png

7AJqdJ.png

  • deviceId:裝置 ID, 需要重點分析的引數,特徵是,以 "B" 開頭;
  • b2c-api-user-token:登入介面返回的,抓包可以發現,登入介面也會有裝置 ID,同理。

搜尋後發現,"B" 後面的內容,是由 v4 介面返回的,然後拼接上 "B" 得到的 deviceId

7AJwwG.png

7AJySB.png

  • organization:某數產品唯一標識;
  • data:加密引數需要分析;
  • ep:加密引數需要分析;
  • 其他:當前網站固定即可,由 compress encode 可知,其他網站的加密方式可能不同。

點開呼叫堆疊,都是由 fp.min.js 檔案載入出來的,進入後,發現經過 ob 混淆了,不過透過搜尋也能輕鬆定位到。我們直接搜尋 compress 或者 encode,注意找與後面數字一致的地方,打下斷點,有兩處地方,重新重新整理,最終定位到的位置如下:

7AJHit.png

ep

該引數由 _0x5223ab 大物件取值生成,搜尋發現 _0x5223ab 物件是經過 _0x484fb9 函式進行賦值操作的:

7AJb7b.png

我們搜尋 _0x484fb9 函式的賦值操作,即可定位到 ep 引數生成的位置:

7AJgVe.png

  • _0x1dbb2a,由上面的 getUid 函式生成,就是 uuid:

7AJhXP.png

  • _0x3f6bcc['publicKey'],透過全域性搜尋發現由 api.js 檔案返回:

    7AJGmw.png

  • rsaEncrypt,經過測試,這裡就是標準的 rsa 加密:

7AJxh6.png

data

_0x1d1d7a 生成,定位就在上面,透過 _0x99c3c2 函式生成,跟進去,返回的 _0x1498fb

7AJ5rO.png

兩次賦值 tn,不過賦值在兩個不同的物件中,只用分析下面的就行,下面的後面會用到,跟進 _0x28c88b

7AJWwf.png

扣下來即可,裡面的 md5 摘要演算法,經過測試也是標準的,繼續往下走,跟進到 _0x8b10c3['default'] 函式中,傳入了兩個物件:

7AJcSc.png

透過一些條件進行篩選,滿足條件的,取一個物件的值與第二個物件的 key,進行 DES 加密,然後新增到新的物件,不滿足條件的直接新增到新的物件中去:

7AJvs3.png

然後進行 gzip 壓縮,提示得相當清楚,我們可以直接引庫:

// 匯入pako庫
const pako = require('pako');

function gzip_decrypt(compressedBase64String){
    // 將Base64字串解碼為二進位制資料
    compressedData = Buffer.from(compressedBase64String, 'base64');

    // 使用pako庫解壓縮二進位制資料
    decompressedData = pako.ungzip(compressedData, { to: 'string' });

    // 輸出解壓縮後的字串
    return decompressedData;
}

function gzip_encrypt(dataToCompress){
    // 將資料轉換為Uint8Array
    dataUint8Array = new TextEncoder().encode(dataToCompress);

    // 使用pako庫進行gzip壓縮
    compressedDataUint8Array = pako.gzip(dataUint8Array);

    // 將壓縮後的資料以Base64編碼
    compressedBase64String = Buffer.from(compressedDataUint8Array).toString('base64');
    return compressedBase64String;
};

_0x1498fb = gzip_encrypt(JSON.stringify(_0x1498fb));

繼續往下走,來到我們返回最後一個 _0x1498fb 值的生成位置,透過 aesEncrypt 函式將上面的壓縮資料 _0x1498fbpriId 加密後生成,進入到這個函式中觀察一下:

7AJD7j.png

是標準的 AES,CBC 加密,加密內容為 gzip 壓縮資料 _0x1498fb,key 是 priId,iv 值固定為 0102030405060708。目前 priId 的生成方式還不知道,發現也是在 _0x5223ab 大物件中,跟我們獲取 ep 的邏輯一樣,發現就在生成 ep 的位置的上面:

7AJOW5.png

將我們生成 ep 傳入的 uuid 型別的引數 _0x1dbb2a 進行擷取然後進行標準的 MD5 加密,就得到了 priId 引數值。

最後就是對明文的分析了,由一些瀏覽器環境、加密引數等資訊構成,透過 sent 接收 ,由 _0xa0ac38(_0x4c1ea4['Protocol']) 返回,分析幾個加密引數:

7AJSjm.png

  • Protocol,與上面包含 DES key 的大物件關聯,保持一致,跟某數滑塊一樣也是動態的 key

    7AJ1m4.png

  • smid,進入 _0xa0ac38 函式,呼叫 _0x4962b8 就在下面 smidgetLocalsmid 函式返回:

7AJP8h.png

7AJRt9.png

扣下來即可,MD5_Encrypt 是標準加密:

function _0xf7f244() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'['replace'](/[xy]/g, function(_0x33e3b5) {
        var _0x49279a = 0x10 * Math['random']() | 0x0;
        return ('x' == _0x33e3b5 ? _0x49279a : 0x3 & _0x49279a | 0x8)['toString'](0x10);
    });
};


function getLocalsmid() {
    var _0xa5b93 = (_0x1ccff8 = new Date(),
    _0x5a4c0d = _0x1ccff8['getFullYear']()['toString'](),
    _0x5b0ca1 = (_0x1ccff8['getMonth']() + 0x1)['toString'](),
    _0x229546 = _0x1ccff8['getDate']()['toString'](),
    _0x539a5b = _0x1ccff8['getHours']()['toString'](),
    _0x29336d = _0x1ccff8['getMinutes']()['toString'](),
    _0x548117 = _0x1ccff8['getSeconds']()['toString'](),
    _0x317aff = _0x5a4c0d + (_0x5b0ca1 = _0x5b0ca1 <= 0x9 ? '0' + _0x5b0ca1 : _0x5b0ca1) + (_0x229546 = _0x229546 <= 0x9 ? '0' + _0x229546 : _0x229546) + (_0x539a5b = _0x539a5b <= 0x9 ? '0' + _0x539a5b : _0x539a5b) + (_0x29336d = _0x29336d <= 0x9 ? '0' + _0x29336d : _0x29336d) + (_0x548117 = _0x548117 <= 0x9 ? '0' + _0x548117 : _0x548117),
    _0x163575 = _0xf7f244(),
    _0x4fa2e9 = _0x317aff + MD5_Encrypt(_0x163575) + '00',
    _0xd556d6 = MD5_Encrypt('smsk_web_' + _0x4fa2e9)['substr'](0x0, 0xe),
    _0x4fa2e9 + _0xd556d6 + 0x0), _0x317aff, _0x163575, _0x4fa2e9, _0xd556d6, _0x1ccff8, _0x5a4c0d, _0x5b0ca1, _0x229546, _0x539a5b, _0x29336d, _0x548117;
    return _0xa5b93;
};
  • box,透過跟值,與 ep 同理,或者透過搜尋就可以發現,就是上一個 v4 介面返回的 deviceId

7AJYeY.png

7AJtyH.png

最後,風控部分就需要自己去探索了,跑多了,如果遇到 'status': '-2',就是觸發了數某的驗證碼,攜帶驗證成功的 rid 就可以正常採集到資料了。

結果驗證

7AJzSZ.png

相關文章