聲明:本文只作學(xué)習(xí)研究,禁止用于非法用途,否則后果自負(fù),如有侵權(quán),請告知刪除,謝謝!
前言:我技術(shù)可能不是很牛逼,但我很會偷懶,怎么簡單怎么來,所以有更好的解密方法可以在評論區(qū)評論~
目標(biāo)網(wǎng)站:這個網(wǎng)站很坑,那個驗(yàn)證字段搜不到,XHR斷點(diǎn)也斷不到,就很奇怪,不過竟然我敢寫這篇文章,就代表我是過了的。
這是網(wǎng)站:
aHR0cHM6Ly90b29scy5taWt1LmFjL25vdmVsYWkv
關(guān)于JS逆向,無非就是:
? ? ? ? 1.尋找參數(shù)
? ? ? ? 2.定位參數(shù)生成的位置
? ? ? ? 3.分析參數(shù)的加密邏輯
? ? ? ? 4.扣代碼調(diào)用
關(guān)于webpack,不了解的可以看下面這篇文章:
https://app.yinxiang.com/fx/970ae39c-9964-4aae-aa96-7e81fee4ef8f
?????????簡單來說就是經(jīng)過webpack打包之后所有資源都變成了模塊,通過“加載器(分發(fā)器)”進(jìn)行調(diào)用。
下面開始逆向過程:
打開目標(biāo)網(wǎng)站,多點(diǎn)擊目標(biāo)網(wǎng)站的“開始生成”按鈕,等待生成成功,比較幾次請求可以發(fā)現(xiàn)請求參數(shù)都是一樣的,而請求頭中有個authsign每次都是不一樣的,故判斷該參數(shù)是驗(yàn)證參數(shù)(實(shí)際上確實(shí)是這個)。
第一步,老規(guī)矩先全局搜索一下,
似乎是沒有賦值的地方,這就很奇怪,
第二步,定位參數(shù)位置??傊瓤纯窗l(fā)起請求的位置(在啟動器里面找)(其實(shí)還有另一種方法:Js Hook,下面再講)
?跳轉(zhuǎn)到這里了,在這里打個斷點(diǎn)。
?再次點(diǎn)擊“開始生成”
?可以發(fā)現(xiàn)這里有個t,里面的headers不知道是什么(一般的headers都是請求頭,所以這個很可疑,先記住這個t),總之按F10跳過函數(shù)繼續(xù)執(zhí)行。
e是個數(shù)組,好像在遍歷這個數(shù)組 ,不管他,反正看不懂,接著摁F10跳過。
到這里之后可以發(fā)現(xiàn)這里請求參數(shù),但是并沒有我們想要的"authsign", 接著摁F10跳過直到看到我們需要的參數(shù)(Authsign)。
可以發(fā)現(xiàn)這里又出現(xiàn)了那個t,里面的headers依舊是那些值。在摁F10跳過函數(shù)
好,跳過之后可以發(fā)現(xiàn)這里這個t里面的headers里面多了個Authsign。所以這個Authsign的生成位置就在這里面。
我們在這里打上斷點(diǎn),取消其他斷點(diǎn),重新點(diǎn)擊“開始生成”,
在這里斷住之后,我們摁F9逐步執(zhí)行(F10是跨函數(shù)執(zhí)行),
逐步執(zhí)行到了這里,打印一下,可以發(fā)現(xiàn)這里就是加密參數(shù)生成的地方,現(xiàn)在已經(jīng)定位到了。
來講講另一種方法:JS Hook
?需要在瀏覽器安裝拓展:油猴(Violentmonkey)
? ? ? ? 打開插件新建腳本:該腳本的作用是在指定請求頭被設(shè)置的時候進(jìn)入debugger狀態(tài)。
? ? ? ? 更多腳本:(https://www.jb51.net/article/241736.htm)
// ==UserScript==
// @name tools.miku
// @namespace Violentmonkey Scripts
// @match https://*/*
// @grant none
// @version 1.0
// @author -
// @description 2022/11/4 08:39:59
// ==/UserScript==
var code = function(){
var org = window.XMLHttpRequest.prototype.setRequestHeader;
window.XMLHttpRequest.prototype.setRequestHeader = function(key,value){
if(key=='Authsign'){
debugger;
}
return org.apply(this,arguments);
}
}
var script = document.createElement('script');
script.textContent = '(' + code + ')()';
(document.head||document.documentElement).appendChild(script);
script.parentNode.removeChild(script);
然后刷新頁面然后啟動該腳本。重新點(diǎn)擊“開始生成”,可以發(fā)現(xiàn)會在這里斷住,說明這個時候“Authsign被設(shè)置上了Header”。
?然后我們開始追棧,追棧的目的也是為了找到加密參數(shù)產(chǎn)生的位置。
調(diào)用堆棧是當(dāng)前代碼的調(diào)用者,以及調(diào)用者的調(diào)用者。說明白點(diǎn)就是,程序執(zhí)行到這里之前的所有操作,而在這里設(shè)置上了“Authsign”值,追棧就是從上往下追,最上面的是當(dāng)前位置,下面的就是上一個調(diào)用位置。還不會的就只能自己去找追棧的教程看。
最后追到這個h.request這里,就和啟動器進(jìn)去的那個是一樣的了,摁F10一步一步看,在哪里添加上了“Authsign”,就跟進(jìn)去,也能定位到加密參數(shù)位置。
接著剛剛的講,定位到加密參數(shù)之后。
通過觀察可以發(fā)現(xiàn)這是一個webpack打包的。webpack打包的都很有特點(diǎn),有個分發(fā)器。
f是,而y在上面定義了,
?y = n.n(f),好。
下面我們開始扣代碼了,先在y=n.n(f)這里打上斷點(diǎn),刷新頁面(模塊都是一開始就加載好的所以我們是刷新頁面而不是點(diǎn)擊“開始生成”)
斷住之后跳到n里面去??梢允髽?biāo)浮在n上面然后跳入該方法,也可以直接在控制臺打印n,然后雙擊跳入該方法。
?
?可以看到這就是webpack所謂的加載器(分發(fā)器)
我們直接復(fù)制這整個js文件, 有的教程可能會說只復(fù)制加載器就可以了,實(shí)則不然,可以發(fā)現(xiàn)我們的y=n.n(f)是n.n的,而n是這個o函數(shù),就相當(dāng)于o.n(f),我們在這個js文件搜索一下,可以發(fā)現(xiàn)下面定義了o.n,所以我們需要全部復(fù)制而不是只復(fù)制加載器。
?新建js文件,粘貼到此。
定義一個全局變量lorder。把最后一個d()改成lorder=o,最一個d()是入口函數(shù),我們用不到,這樣我們就可以在外面使用lorder做o的事了。 然后我們把數(shù)組形式改為對象形式,自己建兩個方法測試一下。
?出異常了。window is not defined。window未定義,我們?nèi)ラ_頭定義一個var window={};?
?然后再運(yùn)行,成功輸出,說明我們的加載器沒有問題。
好,接下來去復(fù)制我們需要的方法,f=n(267),y=n.n(f),也是一樣,刷新頁面跳進(jìn)去,然后復(fù)制方法。我們可以看到n(267)并不是方法。
?這個時候我們跳進(jìn)n,也就是加載器那,打上斷點(diǎn),重新刷新頁面,然后在控制臺打印下標(biāo)為267的函數(shù),e就是函數(shù)數(shù)組,也就是e[267]。
跳進(jìn)去之后發(fā)現(xiàn)里面整個js模塊和其他的不一樣(這個是數(shù)組形式的),而且里面又調(diào)用了一堆函數(shù),這個時候就不管他了,一個一個復(fù)制太累人了,當(dāng)然你要是喜歡一個一個復(fù)制那也可以,不過要記得對象形式前面記得改成對應(yīng)的下標(biāo),也就是這樣
這里就不演示了,我們使用懶人方法,一鍵自吐。先定位到e[267]所在js的開頭,以這種開頭的都是webpack打包的模塊文件。
我們也復(fù)制這個js的模塊,把他放我們的加載器中(注意,我們的加載器js要重新去瀏覽器中復(fù)制下來,得是未更改的,就是沒加lorder的)。
?然后需要用到漁滒大佬的ast逆向。這是大佬的項(xiàng)目地址。
文件 · master · 漁滒 / webpack_ast · GitCode
缺依賴補(bǔ)依賴,我這里在這個ast文件夾創(chuàng)建了一個a.js文件,然后將我們寫的js(加載器以及數(shù)組模塊)復(fù)制到a.js。
然后打開cmd進(jìn)入到該ast文件夾,執(zhí)行一下命令:
node webpack_mixer.js -l a.js -o webpack_out.js
?會報錯的話缺依賴補(bǔ)依賴,然后運(yùn)行成功之后就會多出一個webpack_out.js文件,這個文件就是變成對象形式的模塊。
然后將該js重新復(fù)制回我們自己的js,而且大佬還貼心的幫我們生成了全局變量供我們調(diào)用(export_function = o;),這里再次給大佬點(diǎn)個贊。
?而代碼中的n就相當(dāng)于o函數(shù),而o又賦值給了全局變量export_function,也就是說,n(267)=o(267)=export_function(267),267號方法有了,接著往下看,
我們也定義n(267),以及y,
然后是m函數(shù),在上面定義了,我們復(fù)制過來?,總之就是缺什么補(bǔ)什么
運(yùn)行之后又異常了,看到這個異常就知道是缺函數(shù)了,而我們又不知道缺哪個函數(shù),所以我們在加載器里面加上代碼console.log(c),這里的c就是下標(biāo),也可以說是對象名稱,這樣就知道他是在哪個函數(shù)報錯了,也就知道缺的是哪個函數(shù)了,?
?emmm,51號函數(shù)
老規(guī)矩,進(jìn)到裝飾器中打上斷點(diǎn)?
然后跳進(jìn)51號方法并且將整個js的模塊復(fù)制到加載器中,注意,我們的加載器js要重新去瀏覽器中復(fù)制下來,和上面那個一樣,也是cmd進(jìn)入到ast文件夾,執(zhí)行一下命令:
?node webpack_mixer.js -l a.js -o webpack_out.js
?然后,將輸出的js文件中的模塊復(fù)制到我們自己的js加載器模塊后面就可以了,
?方法之間記得打逗號隔開!
?放好之后保存運(yùn)行,又報錯了。e沒有定義,?
?我們來找找e從哪來的。往上找可以發(fā)現(xiàn)e在這,是方法的參數(shù),
?
?然后我們看看這個方法的參數(shù)是怎么來的,往后看可以發(fā)現(xiàn)是.call(this, n(262), n(51)) ,也就是說,e是n(262),o是n(51)。
好,然后我們也定義一個e=n(262),保存之后再運(yùn)行,又報錯了,r未定義,一樣的,找r在哪定義的
?發(fā)現(xiàn)就在開頭處,
我們也把r定義一下,r=n(56),保存運(yùn)行報錯,o未定義,o就是剛剛那個n(51),我們也定義一下,然后保存運(yùn)行報錯。。
在e = decodeURIComponent(l);這里報錯了,URIError: URI malformed,這個就很奇怪了,看看是不是少了什么東西。好,可以看到這個m的上面確實(shí)是有一些參數(shù)的定義,我們沒有,把他加上去。
?好了,加上去之后依舊是保存運(yùn)行報錯,不過不是剛剛的錯了,這次是d(...)(...)[m(...)] is not a function,看看d在哪定義,有沒有做什么多余的操作。
然后可以看到,d定義之后有個操作,d.a.extend(_.a)?,而_ = n.n(m),m = n(568),
我們也加上這些定義,依舊是保存運(yùn)行報錯,document is not defined,
?這個異常和window不一樣,在上面定義也不管用,總之我們先試一下在上面定義,var document={},可以看到f是打印出來了,但是還是有問題,提示未定義屬性“words”,而words又是f的屬性,
?總之打印然后和瀏覽器的對比一下吧,m("8", "fUV&")是domain,也就是域名,也就是說,document[domain]輸出是undefined,
?看看瀏覽器的,
?所以說,由于我們的程序獲取不到瀏覽器文檔的域名,所以我們直接用死值就好了,改好之后保存運(yùn)行,又報錯了,加密模塊有問題,
?我們打印看看是什么加密,可以看到是AES的加密方式,
參數(shù)已經(jīng)有了,我們引用nodejs的加密庫,
var CryptoJS = require("crypto-js");
crypto-js自行安裝,然后下面的加密也改一下,保存運(yùn)行?
?功夫不負(fù)有心人,終于是加密成功了!放到apipost上試一下,很奇怪,沒有數(shù)據(jù),難道是加密的數(shù)據(jù)有問題?
?經(jīng)過不懈的努力,最后發(fā)現(xiàn)是
?這個f的document[m("3", "jx9[")]有問題,之前說過我們程序訪問不到網(wǎng)站的document,所以我們打印看看是需要什么值,
可以看到他也是domain域名,所以我們這個document[m("3", "jx9[")]也要替換成'tools.miku.ac',保存運(yùn)行,
再次拿著加密的數(shù)據(jù)放到Apipost里,這次請求的比較久(大喜),
可以看到網(wǎng)站返回了base64形式的圖片,終于是成功了!
我貼上核心解密代碼,中間的模塊就不貼了(太多了),文章來源:http://www.zghlxwxcb.cn/news/detail-461337.html
var CryptoJS = require("crypto-js");
var document = {}
var export_function;
!function (e) {
//這里是加載器代碼
}({
//這里是模塊代碼
});
module.exports = export_function;
e = export_function(262)
o = export_function(51)
r = export_function(56)
f = export_function(267)
y = export_function.n(f)
h = (export_function(37), export_function(91), export_function(92), export_function(25), export_function(123), export_function(49))
d = export_function.n(h)
m = export_function(568)
_ = export_function.n(m)
d.a.extend(_.a)
var n, l, h = ["jsjiami.com.v6", "jsjSiamtirN.coEmh.Fulv6YWfbWzOB==", "bALCr8KoHEA=", "w7BQDcKBPMO7", "wqw8W8OFT1XDnw==", "TcKFbsOIw7oTwqnDrQ==", "wqM3w7c=", "w6TCqsKDw6PCsMOaRA==", "wq9Qw7rDpcKRZg==", "w7Msw5zDtsORCsOJIw==", "OlPDoQ==", "IsOIWMOowpvCtHdD"];
n = h,
l = 448,
function (e, t, o, r) {
if ((t >>= 8) < e) {
for (; --e;)
r = n.shift(),
t === e ? (t = r,
o = n.shift()) : o.replace(/[StrNEhFulYWfbWzOB=]/g, "") === t && n.push(r);
n.push(n.shift())
}
}(++l, 114688);
var m = function t(n, c) {
n = ~~"0x".concat(n);
var l = h[n];
if (void 0 === t.RVAulw) {
!function () {
var t = "undefined" != typeof window ? window : "object" === (void 0 === e ? "undefined" : Object(r.a)(e)) && "object" === (void 0 === o ? "undefined" : Object(r.a)(o)) ? o : this;
t.atob || (t.atob = function (e) {
for (var t, n, o = String(e).replace(/=+$/, ""), r = 0, c = 0, l = ""; n = o.charAt(c++); ~n && (t = r % 4 ? 64 * t + n : n,
r++ % 4) ? l += String.fromCharCode(255 & t >> (-2 * r & 6)) : 0)
n = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(n);
return l
}
)
}();
t.hRdDHj = function (e, t) {
for (var n, o = [], r = 0, c = "", l = "", h = 0, d = (e = atob(e)).length; h < d; h++)
l += "%" + ("00" + e.charCodeAt(h).toString(16)).slice(-2);
e = decodeURIComponent(l);
for (var m = 0; m < 256; m++)
o[m] = m;
for (m = 0; m < 256; m++)
r = (r + o[m] + t.charCodeAt(m % t.length)) % 256,
n = o[m],
o[m] = o[r],
o[r] = n;
m = 0,
r = 0;
for (var _ = 0; _ < e.length; _++)
r = (r + o[m = (m + 1) % 256]) % 256,
n = o[m],
o[m] = o[r],
o[r] = n,
c += String.fromCharCode(e.charCodeAt(_) ^ o[(o[m] + o[r]) % 256]);
return c
}
,
t.gjhasZ = {},
t.RVAulw = !0
}
var d = t.gjhasZ[n];
return void 0 === d ? (void 0 === t.PPSXqK && (t.PPSXqK = !0),
l = t.hRdDHj(l, c),
t.gjhasZ[n] = l) : l = d,
l
}
_ = d()()[m("0", "ZLEd")]()[m("1", "1)V^")]()
f = y.a[m("2", "ise7")]("" + _ + 'tools.miku.ac');
authsign=f + "." + CryptoJS.AES.encrypt(_, 'tools.miku.ac')[m("9", "6lDQ")]()
console.log(authsign)
本篇文章是給自己做個記錄,當(dāng)然也歡迎大家學(xué)習(xí)交流。文章來源地址http://www.zghlxwxcb.cn/news/detail-461337.html
到了這里,關(guān)于JS逆向 webpack解密的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!