JS逆向之基礎(chǔ)定位技巧

篇幅有限

完整內(nèi)容及源碼關(guān)注公眾號(hào):ReverseCode,發(fā)送

當(dāng)我們拿到一個(gè)網(wǎng)站時(shí),首先就是抓包定位加密參數(shù)的實(shí)現(xiàn),本文將通過常用的定位方案結(jié)合實(shí)際案例完成對(duì)加密參數(shù)的分析。

搜索關(guān)鍵參數(shù)

這是最常見也是最簡單的定位方案,F(xiàn)12打開網(wǎng)站控制臺(tái)后,Ctrl+Shift+F打開搜索面板,比如搜索password參數(shù)或者submit函數(shù)

password:`,`password=`,`password =`請(qǐng)求url,搜索方法`var submit`或者`function submit`或者`submit:

to8to

圖片

搜索password位置太多,由于抓包請(qǐng)求是new_login.php

圖片

在Element面板中搜索new_login.php,ctrl+shift+f 搜索loginCheck

圖片

該方法中jq('#rsa_userNum').val(rsaString(password));,調(diào)用rsaString方法加密password

function rsaString(str) {
    return encodeURIComponent(RSAUtilszb.encryptfun(str));
}

進(jìn)入encryptfun定義的js中,rsa加密最少2000行,該方法不過163行,拷貝該js通過編程貓專用工具中的JS調(diào)試工具,加載代碼,報(bào)錯(cuò)引用錯(cuò)誤: window 未定義,添加var window = this;,報(bào)錯(cuò)引用錯(cuò)誤: JSEncrypt 未定義,添加原js中var JSEncrypt = JSEncryptExports.JSEncrypt;,報(bào)錯(cuò)引用錯(cuò)誤: JSEncryptExports 未定義,搜索var JSEncryptExports,將var JSEncryptExports = {};添加到JS調(diào)試工具,報(bào)錯(cuò)類型錯(cuò)誤: JSEncrypt is not a constructor

圖片

嘗試打上斷點(diǎn),但是每次都不能進(jìn)入斷點(diǎn),說明肯定是動(dòng)態(tài)加載的js,且每次刷新js后綴會(huì)有時(shí)間戳。勾選Disable cache,打開fiddler抓包,將js拷貝到本地實(shí)現(xiàn)http欺騙,選中該請(qǐng)求點(diǎn)擊AutoResponder-Add Rule下拉選擇Find a File,找到本地保存的js并開啟規(guī)則

圖片

由于每次js請(qǐng)求地址不一樣,使用正則匹配regex:https://static\.to8to\.com/gb_js/to8torsaszb\.js\?_=\d+并保存規(guī)則重新發(fā)起請(qǐng)求https://static.to8to.com/gb_js/to8torsaszb.js?_=1628128571412,使用本地js欺騙網(wǎng)絡(luò)請(qǐng)求js

圖片
圖片

將整個(gè)js格式化找到之前報(bào)錯(cuò)JSEncrypt is not a constructor是從上面的壓縮的js中export出來的

圖片

將上面壓縮的代碼添加到編程貓的JS調(diào)試工具中加載代碼,報(bào)錯(cuò)引用錯(cuò)誤: navigator 未定義,添加var navigator = {},報(bào)錯(cuò)引用錯(cuò)誤: window 未定義,添加var window =this,因?yàn)槿绻?code>window ={}報(bào)錯(cuò)ASN1 未定義,而用this則可以拿到當(dāng)前js中所有的變量函數(shù)。

圖片

dom元素事件監(jiān)聽

通過控制臺(tái)的Elements中的Event Listeners逐個(gè)排除按鈕的Remove節(jié)點(diǎn),直到最后一個(gè)Event Listeners使按鈕無效,拿到該按鈕真正生效的js位置。

中煙新商盟

圖片

以下通過dom元素事件監(jiān)聽實(shí)現(xiàn)對(duì)該j_mcmm加密參數(shù)邏輯定位分析。

圖片

以上通過盡可能多的地方打上斷點(diǎn),監(jiān)聽元素事件定位到j(luò)smain-9826b285f8fad5a5.js,左下角格式化js后添加斷點(diǎn),在js頁面ctrl查看所有變量值

圖片

鼠標(biāo)懸停,或者控制臺(tái)打印出來,點(diǎn)擊進(jìn)入方法聲明時(shí)打上斷點(diǎn),為同一行中的函數(shù)打上斷點(diǎn),F(xiàn)8單步調(diào)試,完成加密參數(shù)的定位

圖片

xhr斷點(diǎn)

通過定位發(fā)包函數(shù)跟棧,復(fù)制網(wǎng)址請(qǐng)求路徑到Sources下的XHR/fetch Breakpoints,支持正則。

七麥數(shù)據(jù)

圖片

通過關(guān)鍵加密參數(shù)analysis搜索無果,嘗試在Sources中加入XHR斷點(diǎn),以請(qǐng)求路徑作為斷點(diǎn)內(nèi)容

圖片

XHR斷點(diǎn)后追溯調(diào)用棧,查看每個(gè)調(diào)用棧的出入?yún)⑹欠癜用芎蟮?code>analysis,直到進(jìn)入Promise異步l.request,單步調(diào)試到n.then(t.shift(), t.shift()),then作為Promise的異步函數(shù),promise.then(onCompleted, onRejected);,而shift()通過逐條調(diào)用t中的方法,參數(shù)是上一個(gè)方法的返回值,同時(shí)刪除該方法,相當(dāng)于隊(duì)列先進(jìn)先出??刂婆_(tái)打印t,逐個(gè)方法進(jìn)入打上斷點(diǎn)

圖片

l.prototype.request中暫時(shí)還沒生成analysis加密參數(shù),逐個(gè)進(jìn)入t方法中

圖片

執(zhí)行完r().interceptors.request.use該方法后生成的a就是analysis,觀察該代碼中的邏輯完成加密分析。a=(0,n.cv)((0,n.oZ)(r, l))作為逗號(hào)表達(dá)式,由上圖分析n.cv和n.oZ是函數(shù),r和l是變量,可以還原為n.cv(n.oz(r,l))

通過控制臺(tái)獲取n中的函數(shù)

window = global;
window.document = {
    cookie: ''  // 這邊帶上自己的cookie
}
function i(e) {
    var t, a = (t = "",
        ["66", "72", "6f", "6d", "43", "68", "61", "72", "43", "6f", "64", "65"].forEach((function (e) {
                t += unescape("%u00" + e)
            }
        )),
        t);
    return String[a](e)
}
function s() {
    return unescape("861831832863830866861836861862839831831839862863839830865834861863837837830830837839836861835833".replace(/8/g, "%u00"))
}
var n = {
    oZ: function g(e, t) {
        t || (t = s());
        for (var a = (e = e.split("")).length, n = t.length, o = "charCodeAt", r = 0; r < a; r++)
            e[r] = i(e[r][o](0) ^ t[(r + 10) % n][o](0));
        return e.join("")
    },
    cv: function h(e) {
        return function (e) {
            try {
                return btoa(e)
            } catch (t) {
                return Buffer.from(e).toString("base64")
            }
        }(encodeURIComponent(e).replace(/%([0-9A-F]{2})/g, (function (e, t) {
                return i("0x" + t)
            }
        )))
    },
    ej: function u(e) {
        var t, a = new RegExp("(^| )" + e + "=([^;]*)(;|$)");
        return (t = document.cookie.match(a)) ? unescape(t[2]) : null
    }
}

由于btoa本質(zhì)就是base64加密,通過引入CryptoJS.pad.js后

function base64(data) {
 var wordArray = CryptoJS.enc.Utf8.parse(data);
 var base64_data = CryptoJS.enc.Base64.stringify(wordArray);
 return base64_data
}
var n = {
    oZ: function g(e, t) {
        t || (t = s());
        for (var a = (e = e.split("")).length, n = t.length, o = "charCodeAt", r = 0; r < a; r++)
            e[r] = i(e[r][o](0) ^ t[(r + 10) % n][o](0));
        return e.join("")
    },
    cv: function h(e) {
        return function (e) {
            try {
                return base64(e)
            } catch (t) {
                return Buffer.from(e).toString("base64")
            }
        }(encodeURIComponent(e).replace(/%([0-9A-F]{2})/g, (function (e, t) {
                return i("0x" + t)
            }
        )))
    },
    ej: function u(e) {
        var t, a = new RegExp("(^| )" + e + "=([^;]*)(;|$)");
        return (t = document.cookie.match(a)) ? unescape(t[2]) : null
    }
}

將try/catch中邏輯還原

var l = "00000008d78d46a"
var d = "@#"
var e = {
    // url: "/rank/indexPlus/brand_id/1",
    url: "/rank/indexPlus/brand_id/" + pg,    // 1.免費(fèi)榜 0.付費(fèi) 2.暢銷榜
    baseURL: "https://api.qimai.cn",
}
var c = {
    default: function On(e) {
        this._init(e)
    }
}
var u = "synct"
var t = (0, n.ej)(u);
var m = "syncd"
var f = f = c.default.prototype.difftime = -(0, n.ej)(m) || +new Date - 1e3 * t
var o = +new Date - (f || 0) - 1515125653845
var r = []
r = r.sort().join(""), r = (0, n.cv)(r), r += d + e.url.replace(e.baseURL, ""), r += d + o, r += d + 1,
a_ = (0, n.cv)((0, n.oZ)(r, l))
return a_
圖片

Initiator棧追蹤

Network下的發(fā)包請(qǐng)求的Initiator,如jquery堆棧的頂層斷點(diǎn)(可能會(huì)請(qǐng)求多次,找到發(fā)包請(qǐng)求時(shí)進(jìn)入的斷點(diǎn)),重新請(qǐng)求找到堆棧中屬于目標(biāo)網(wǎng)站的js格式化斷點(diǎn)。

升學(xué)e網(wǎng)通

登錄抓包,打開Initiator,進(jìn)入堆棧頂層定位的代碼行

圖片

打上斷點(diǎn),查看右側(cè)調(diào)用棧,逐個(gè)方法往底層去調(diào)用,直到react庫js找到了preLogin,找到出現(xiàn)password的位置,打上斷點(diǎn)

圖片

再次登錄時(shí),進(jìn)入斷點(diǎn),找到password

圖片

進(jìn)入加密方法中,aes加密

圖片

打開WT-JS中的Crypto類復(fù)制key和iv,輸出以HEX的十六進(jìn)制格式,對(duì)比結(jié)果是標(biāo)準(zhǔn)的AES加密。

圖片

基于base64或十六進(jìn)制的AES加解密實(shí)現(xiàn)見aes.js

圖片

長房集團(tuán)

圖片

搜索j_password后打斷點(diǎn),重新登錄

圖片

進(jìn)入desEncrypt中,大致加密完成邏輯就在該函數(shù)中

圖片

加密邏輯中首先根據(jù)SECURITYKEY.get()獲取到key,首先通過請(qǐng)求后端拿到str,判斷加密類型是否為aes后截取字符串通過toHexString轉(zhuǎn)成十六進(jìn)制拿到key和iv和security

圖片

整理完邏輯扣出js報(bào)錯(cuò)CryptoJS is not defined,點(diǎn)擊進(jìn)入CryptoJS.AES.encrypt扣出來aes.js源碼

function getdes(encodeType) {
    // 請(qǐng)求"/resource/js/session.jsp?_=1628210376229"返回
    var str = "E55A433905551AC39DB3165591D9CD74";
    if (encodeType == null || encodeType == 'aes') {
        if (str.length < 32) {
            str += "abcdefghijklmnopqrstuvwxyz1234567890"
        }
        str = str.toUpperCase();
        var key = {};
        key.key = str.substring(0, 16);
        key.iv = str.substring(16, 32);
        key.security = "\u4435\u5320\u4d35"
    } else {
        if (str.length < 16) {
            str += "abcdefghijklmnopqrstuvwxyz"
        }
        str = str.toUpperCase();
        var key = {};
        key.key = toHexString(str.substring(0, 8));
        key.iv = toHexString(str.substring(8, 16));
        key.security = "\u4445\u5320\u4d45"
    }
    return key
}
function getPwd(value, type) {
    var keyObj = {};
    if (type == null || "aes" == type.toLowerCase()) {
        keyObj = getdes()
        value = CryptoJS.AES.encrypt(value, CryptoJS.enc.Utf8.parse(keyObj.key), {
            iv: CryptoJS.enc.Utf8.parse(keyObj.iv)
        }).toString()
    } else {
        keyObj = getdes()('des');
        value = CryptoJS.DES.encrypt(value, CryptoJS.enc.Hex.parse(keyObj.key), {
            iv: CryptoJS.enc.Hex.parse(keyObj.iv)
        })
    }
    return keyObj.security + value
}
圖片

安裝編程貓插件

  • fiddler 版本必須 >= v4.6.3,復(fù)制Fiddler 編程貓專用插件到fiddler程序目錄下的Scripts目錄中示例: C:\Program Files (x86)\Fiddler2\Scripts

愛奇藝

  1. 覆蓋原函數(shù)
  function xxx(){
      console.log("1111")
  }
  var xxx_ = xxx;
  xxx = function(){
      console.log("2222")
  }
  window.alert = function(){console.log("?")}
  console.clear = function(){console.log("?")}
  setInterval = function(){}
  1. Object.defineProperty替換對(duì)象屬性(getter.setter)
  (function () {
      var a = "";
      Object.defineProperty(document, 'cookie', {
          set: function (val) {
              console.log('Hook捕獲到cookie設(shè)置->', val);
              a = val;
              return val;
          },
          get: function(){
              return a;
          }
      });
  })();
  document.cookie = "1"  // 設(shè)置
  document.cookie  // 獲取
  1. hook的時(shí)機(jī)在控制臺(tái)注入的hook,刷新網(wǎng)頁就失效了,過濾Network的js找到第一個(gè)加載的js,右鍵Open in Sources panel格式化,第一行斷點(diǎn),不過有些cookie可能異步可能在html中js生成,在控制臺(tái)中注入以上hook,清除cookie,手動(dòng)注入hook,控制臺(tái)中找到VM虛擬機(jī)找到我們的hook的js打上斷點(diǎn),,每次hook都會(huì)經(jīng)過set,右側(cè)就可以查看調(diào)用棧,追溯cookie的來源與加密方式。(有可能注入hook的時(shí)機(jī)會(huì)晚于部分異步請(qǐng)求或者h(yuǎn)tml中的js)
圖片
  1. 利用fiddler代理所有請(qǐng)求替換響應(yīng),編程貓專用工具注入hook
圖片
(function () {
    'use strict';
    Object.defineProperty(document, 'cookie', {
        set: function (val) {
            if (val.indexOf("__dfp") != -1) {
                debugger;
            }
            console.log('Hook捕獲到cookie設(shè)置->', val);
            return val;
        }
    });
})();
圖片

接下來查看調(diào)用棧,最終保存到window.name中。

(function () {
    'use strict';
    var a = "";
    Object.defineProperty(window, 'name', {
        set: function (val) {
            debugger;
            a = val;
            console.log('Hook捕獲到cookie設(shè)置->', val);
            return val;
        }, 
        get: function(){
            return a;
        }
    });
})();

重新進(jìn)入iqiyi,斷點(diǎn)完成hook定位從而可以根據(jù)調(diào)用棧分析cookie的生成邏輯。

本文由博客群發(fā)一文多發(fā)等運(yùn)營工具平臺(tái) OpenWrite 發(fā)布

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容