逆向實戰33——某東登入引數與流程分析(包含滑塊)

始識發表於2024-03-08

宣告

本文章中所有內容僅供學習交流,抓包內容、敏感網址、資料介面均已做脫敏處理,嚴禁用於商業用途和非法用途,否則由此產生的一切後果均與作者無關,若有侵權,請聯絡我立即刪除!

目標網站

aHR0cHM6Ly9wYXNzcG9ydC5qZC5jb20vbmV3L2xvZ2luLmFzcHg/UmV0dXJuVXJsPWh0dHBzJTNBJTJGJTJGd3d3LmpkLmNvbSUyRg==

前言

上次介紹了H5st的加密。這次來研究一下模擬登陸。即登入+滑塊
這裡先貼一張圖 下圖所示 是登入要的所有引數。至於所得所有引數 怎麼來的? 如何來的?請往下看。

有能力的同學可以加下本人星球哦

https://t.zsxq.com/17OvVkJha

本文詳細原文

https://mp.weixin.qq.com/s/D2MTec5F92GoSho0af26ww

介面分析

引數過多。這裡直接步入正題。
首先開啟一個無痕瀏覽器(這很重要)

關注一下這個slide/g.html請求。 發現這裡只有一個 e 請求是會改變的。但是好像寫死也沒問題。

如下圖然後往後看有很多個請求。但請求都大同小異啊。這裡不賣關子了。請求是繫結的滑鼠事件。每次移動滑鼠都有請求發過來。


進棧點看看。

這裡發現d的結果已經出來 往上走。

這裡發現 C(a)的值就是生成d的引數。
這裡 傳參a

c函式如下

這裡我扣下來了。

C = function (a) {
    if (void 0 == a || null == a)
        return null;
    var b = "{", e;
    for (e in a)
        b += "'" + e + "':",
            b = "string" == typeof a[e] ? b + ("'" + a[e] + "'") : b + a[e],
            b += ",";
    b = b.substring(0, b.length - 1);
    return E(b + "}")
}
E = function (a) {
    this.tdmovebit = function () {
        var a = 10
            , b = 20
            , c = 30;
        ++a;
        a++;
        a = ++a + ++b + c++ + a++;
        return d + a - 76
    }
    ;
    var b = "23IL";
    a = encodeURIComponent(a);
    var e = ""
        , c = "";
    b += "<N01c7KvwZO56RSTAfghiFyzWJqVabGH4PQdopUrsCuX*xeBjkltDEmn89.-";
    var d = 0;
    var g = "";
    do {
        var h = a.charCodeAt(d);
        d = this.tdmovebit(d);
        var f = a.charCodeAt(d);
        d = this.tdmovebit(d);
        e = a.charCodeAt(d);
        d = this.tdmovebit(d);
        var k = h >> Math.round(((19 << 43) / 90 ^ 34) / 214);
        h = (h & 3) << 4 | f >> 4;
        var l = (f & 15) << 2 | e >> 6;
        c = e & Math.round(((19 << 43) / 90 ^ 34) / 6) - 4;
        isNaN(f) ? l = c = Math.round(((19 << 43) / 90 ^ 34) / 6) - 3 : isNaN(e) && (c = Math.round(((19 << 43) / 90 ^ 34) / 6) - 3);
        g = g + b.charAt(k) + b.charAt(h) + b.charAt(l) + b.charAt(c);
        k = h = l = c = h = f = e = ""
    } while (d <= a.length);
    return g + "/"
}

let a = {
    "bizId": "passport_jd_com_login_pc",
    "elementId": "loginname",
    "seq": "sniffV",
    "sessionId": "294576929414652485",
    "special": "0",
    "sp": 1,
    "version": "1.0",
    "val": "",
    "ctime": 1709542681074
}
console.log(C(a));

拿到這個 d值了。
這裡繼續往下 下斷點。

然後我們移動滑鼠就會有請求發過來。
發現這個地方 就是監聽滑鼠的事件。

這裡不扯這個了。繼續回頭來走這個流程
然後我們輸完密碼 發現又有個 slide/s.html請求

然後它的返回值是fail 意思是滑動失敗

然後隨後又發了個slide/g.html請求。

那這樣的話。我們是不是可以簡單梳理一下。

  • g請求拿驗證碼
  • s請求返回結果

那這個g請求中的e是什麼?
我們找一下 第二個堆疊


那這個方法中如何獲取eid
我們繼續追棧進去

繼續進這個 a = getJdEid()['eid']
好了 進去一個虛擬機器了


然後發現 這個 eid 居然在這個fcf.html 請求中

然後要訪問這個請求 需要三個加密方法。
分別為 a, g , d.

嫌麻煩可以把 e 引數寫死 能不能成功我就不知道了。

fcf介面加密分析

這裡不廢話了。打斷點 開幹。
這裡不知道怎麼找。直接追棧吧。就直接從最上面的棧追。然後一步一步往上看。
如下圖所示 就是加密的地方。

這裡我們一個一個看把

z(a)引數分析



如上圖 傳遞了很多引數。其中要注意的是 fc fp jtb

這裡其實可以全拉下來補環境。但是我感覺有點麻煩了。
可以一個一個去扣下。
這裡tdencrypt 函式很好扣。
如下圖所示

這裡摳出來了。但是傳參我們還沒扣。
fc
fc是eid 測試下來可以為空;
**fp **
fp 如下圖

繼續往上追

然後我們全域性搜尋_JdJrTdRiskFpInfo

然後我們進去get這個方法

把這個方法全扣下來
然後補一下環境。

ok 這樣就出結果了
jtb
jtb就是jd_shadow__

這裡往上找 終於找到加密的地方是個自執行函式。用了什麼加密就不說了 一目瞭然

這裡給他扣下來。發現扣下來執行結果是undefined
這裡有兩種結果。第一種是異常了。第二種是加密的值就是undefined

這裡發現 加密了r值 而r一開始是個陣列,新增了一些陣列然後拼接成字串。

然後修修補補 最後修改一下 執行就行。

n(g,d)引數分析


如上圖 g 和 d 引數都被賦值給了 n。
g
n.g = td_collect.tdencrypt(u); 這裡就直接偽造u就可以了 tdencrypt上文 搞z的時候已經寫好了。

d
n.d = _JdJrRiskClientCollectData;

但是 td_collect.collect() 這個 應該是很多環境檢測。我們進去看看。
至於我為什麼篤定是環境檢測。因為上一篇h5st中collect函式也是。
如下圖 是collect() 函式。這裡我們直接滑到最低。

直接把g全弄下來構造環境即可。

至此 a, g , d 全部搞定。全部搞定就可以獲得eid了。

這裡扣不扣程式碼和補環境差不多 都是兩千多行。
這部分的程式碼 我放到星球裡了。

滑塊分析

s請求。
這裡我們先看 s 請求。

這裡我們需要逆向的引數是 d 其他的值 有的是透過 g 有的是可以寫死。
這裡 o 是使用者賬號 但是密碼不在這個請求裡。

這裡我們直接進 下圖這個棧 透過這個棧跟。


然後到達 submit 函式

繼續追 到 a['getCoordinate'](b) 這個地方就是生成d的地方。其他值 生成的邏輯大家也可以看看。
這裡我們發現 還是沒有發現密碼的引數是哪個?

這裡可以把 這個函式摳出來。
這個摳出來的邏輯 巨!!!簡單。不說了 如下圖所示

g請求
搞完了s 我們來繼續請求g 這裡之所以倒退 主要是其他值生成的邏輯 我們可以提前看看。就可以找到要拿哪些需要的引數了
先訪問這個請求。

這裡值得注意的是這個 challenge 是驗證碼ID 也需要提出來。

其他的引數更簡單了 提取出s需要的引數就行了。

密碼加密

但是這裡有另一個問題。
s請求和 g 請求都搞了。但是我們一直沒找到密碼的加密引數啊
這裡重新走一遍流程。
這裡發現,我們滑鼠即使不動。在輸入密碼的時候 依然有很多請求發過去。那這個是怎麼回事?

這裡繼續在這個C(a)這個地方打斷點。並且埋點,看看整體的邏輯。

console.log(a,">>>>>",C(a));false

這裡為什麼這麼寫就不用多說了 。之前xhs寫過了。

這裡 elementId: "nloginpwd" 暴露了這是密碼加密的地方。並且傳遞密碼還是透過每次的鍵盤事件。

但是這每次傳。總不可能伺服器接受這麼多次吧。肯定還有個統一提交的地方。

登入提交

我們完整的再走一次流程。把滑塊也給滑過去。
此時發現一個請求uc/loginService 這個請求應該大機率是提交的地方。

這裡我們再走一下流程搜尋 aksParamsU aksParamsB

如下圖。
aksParamsU


aksParamsB

這裡aksParamsU aksParamsB發現都是公用的encrypte 函式。
這裡我也複製下來了

    function encrypte(data) {
        if (checkInitStatus() && !isBlank(data)) {
            try {
                // const data_ = decodeURIComponent(data)
                return encodeURIComponent(SummerCryptico.encryptData(publicKey, data + ""))
            } catch (e) {
                return data
            }
        } else {
            return data
        }
    }

扣下來大概兩千多行。

這裡可以發現 除了常規加密 還帶了一個 publicKey
這裡public的生成經過斷點分析後可知由最開始初始化的init請求提供。


這裡我們把data 複製下來分析一下。
這個引數還挺多的。

這裡往上找堆疊


(發現和上文我們複製的引數是一樣的。)

這裡繼續往這個getEntryptPwd下走
這裡在走斷點的時候發現 文中的 publickey如下所示

然後把函式扣下來加密 就是密碼的加密了。


大概隨便補一下就出來了。

總結

很好 這裡該解密的都解密的都解密出來了。
這裡簡單敘述一下流程
其實就三個請求。

  1. g請求拿到驗證碼
    a. 但是 e呢 需要訪問fcf請求
    b. fcf請求需要逆向e,g,d
  2. s請求根據g請求返回結果
    a. e引數同上
    b. d引數是透過JDJRValidate.getCoordinate 加密軌跡得到。
  3. loginService請求 發起請求。並且整個從引數加密
    a. aksParamsU 與 aksParamsB都是統一加密方式。不同的是加密的明文
    b. 明文的密碼 又是一段加密程式碼。

結語

本文關於程式碼部分的加密引數。可以看下文詳解。

https://wx.zsxq.com/dweb2/article?groupId=51112885255144

相關文章