原貼地址:手撕某解析網站,獲取真實播放地址(保姆級講解) - 『脫殼破解區』 - 吾愛破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
文章的網址做了脫敏處理,筆者用了BASE64編碼,請自行解碼。
文章里面的Fiddler使用了編程貓專用插件,請自行百度下載。
一、聲明
本文章中所有內容僅供學習交流,抓包內容、敏感網址、數據接口均已做脫敏處理,嚴禁用於商業用途和非法用途,否則由此產生的一切后果均與作者無關,若有侵權,請聯系我立即刪除!
二、前言
最近追上了電視劇,為了方便自己,准備去抓幾個接口,於是就度娘一下,除了廣告還有這個么多,很好,那我們就拿排名第一的分析分析,文才不佳,還請各位將就着
<ignore_js_op>
三、目標
主頁:dHYuaHp3ZGQuY24=
接口:aHR0cHM6Ly9qeC5wYXJ3aXguY29tOjQ0MzMvcGxheWVyL2FuYWx5c2lzLnBocD92PWh0dHBzOi8vdi5xcS5jb20veC9jb3Zlci96cjVhNjdsMzMzZWh6dTkuaHRtbA==
四、無限debugger
F12打開控制台后,會進入一個無限debugger,為了我們以后的調試,我們不得不將它干掉
<ignore_js_op>
我們回溯到這里,發現調用 setInterval 方法,該方法可按照指定的周期(以毫秒計)來調用函數或計算表達式,
很明顯,它調用了check,注意check函數,是將debugger 傳遞給了構造方法constructor,所以這里hook掉constructor
<ignore_js_op>
我們打開fiddler,開啟hook代碼如下
|
1
2
3
4
5
6
7
|
Function.prototype.constructor_ = Function.prototype.constructor;
Function.prototype.constructor =
function
(a) {
if
(a ==
"debugger"
) {
return
function
(){};
}
return
Function.prototype.constructor_(a);
};
|
<ignore_js_op>
五、抓包分析
ok,我們干掉了煩人的無限debugger,我們抽取接口地址:
aHR0cHM6Ly9qeC5wYXJ3aXguY29tOjQ0MzMvcGxheWVyL2FuYWx5c2lzLnBocD92PWh0dHBzOi8vdi5xcS5jb20veC9jb3Zlci96cjVhNjdsMzMzZWh6dTkuaHRtbA==
重新加載一篇,很明顯視頻可以正常播放,而且我們的目標播放地址也能看到了
<ignore_js_op>
接下來我們分析播放地址是怎么來的,我們用接口地址訪問,整個頁面非常簡單,我們看看接口地址返回了什么
<ignore_js_op>
一個加密的視頻地址,一個加密混淆的abc函數代碼
ok,我們來看看abc函數
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
function
abc(){
[
"sojson.v4"
][
"\x66\x69\x6c\x74\x65\x72"
][
"\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72"
]((([
"sojson.v4"
]+[])[
"\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72"
]
[
"\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65"
][
"\x61\x70\x70\x6c\x79"
]
(
null
,
"118M97i114L32C95D112e114M32G61l32C36A40O39B109e101w116m97N91Y110y97I109I101J61q34E118i105N101h119R112I111G114T116z34Z93o39m41o46S97o116b116T114C40Q39r105I100m39O41f46U1
14P101B112t108d97g99M101X40Z39P118u111s100A95O39A44e39g39J41Y59l10b32j32e32e32l32Q32n32f32t32a118D97w114r32U95V112F117z32K61r32u36G40g39d109N101d116S97V91d99M104m97q114M115y10
1f116h61K34w85c84X70z45j56J34p93O39e41V46G97P116a116d114P40O39i105B100S39x41s46l114Y101I112E108w97m99h101q40y39Q118Y111A100D95p39V44X39z39a41k59d10l32M32v32d32n32C32G32G32y32Y
118K97O114u32v95f112e117z65W114w114y32i32f61T32Y91i93E59x10x32v32T32e32F32b32U32r32a32i118t97z114A32W95U110r101v119r65O114i114Q32O61b32E91L93N59u10R32u32A32c32y32R32I32o32Y32W
118A97L114D32l95E99E111W100P101N32D32N32o61n32d39r39Q59g10e32K32g32w32X32k32b32j32p32A10r32X32X32r32U32D32j32q32F32v102U111D114X40g118e97v114V32B105N61c48U59v105z60L32m95z112Z
117S46y108a101b110K103a116h104H59O32A105Z43o43m41Y123Q10e32c32v32B32G32E32J32a32W32t32O32J32s32j10C32T32K32A32I32A32h32Z32V32J32T32b32C32A95X112e117z65C114t114M46Z112J117P115l
104d40w123z32e39j105C100S39Q58a95S112I117K91j105n93t44k32P39w116Q101p120K116x39p58p32R95z112D114A91S105Z93U32x125Q41J59i10H32c32l32G32D32r32A32R32L32G125F10Q32h32M32W32j32E32Y
32m32J32P10L32v32y32A32C32t32U32B32F32B47D47G23545R23494p38053e37325J26032k36827g34892u25490S24207C10x32l32G32B32d32m32E32i32u32i95d110c101L119a65o114y114k32C61p32k95Y112l117v
65w114h114i46w115f111r114A116X40T80Z65Q82U46v99c111s109f112g97f114p101g40q34x105x100U34i41e41i59J10a32U32m32y32y32X32s32K32W32P10J32V32H32w32E32x32U32Q32s32M102B111V114Y40j118
e97h114K32v105L61J48h59d105G60A32g95B110Z101F119J65v114x114v46B108c101j110g103W116R104F59f32a105p43b43f41b123o10Q32Z32I32J32e32A32m32k32r32p32b32Q32W32G10P32J32H32T32F32V32U32
V32g32W32p32A32n32W95q99w111Y100k101e43b61C95A110p101v119I65k114F114V91d105G93N91D39y116d101X120u116H39t93a59b10h32Z32D32E32F32S32K32D32a32l125J10O32P32n32b32c32K32u32S32b32u9
9t111x110K102Q105h103D46T117p114L108l32q61u32t32H80X65I82r46Q115H101x99D114Y101a116V40C99c111I110h102K105f103X46Q117N114w108E44f32f95U99b111W100R101a44L32u116U114P117q101I41w5
9"
[
"\x73\x70\x6c\x69\x74"
](/[a-zA-Z]{1,}/))))(
"sojson.v4"
);
|
我們先把字符串編碼解決一下,大概就是這個樣子
|
1
|
[
'sojson.v4'
]+[])[
"constructor"
][
'fromCharCode'
][
'apply'
](
null
,
"123456..."
[
'split'
](/[a-zA-Z]{1,}/))))[
'sojson.v4'
]
|
根據個人理解,上面幾乎可以等於以下代碼
|
1
|
''
.constructor.fromCharCode.apply(
null
,
"123456..."
.split(/[a-zA-Z]{1,}/))
|
大概意思就是把"123456..."轉換成字符數組“1”“2”...,然后用fromCharCode方法轉換成字符串
|
1
|
String.fromCharCode(“1”“2”...)
|
我們在控制台輸出一下
<ignore_js_op>
ok,我們得到如下字符串代碼
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
(
function
anonymous(
) {
var
_pr = $(
'meta[name="viewport"]'
).attr(
'id'
).replace(
'vod_'
,
''
);
var
_pu = $(
'meta[charset="UTF-8"]'
).attr(
'id'
).replace(
'vod_'
,
''
);
var
_puArr = [];
var
_newArr = [];
var
_code =
''
;
for
(
var
i=0;i< _pu.length; i++){
_puArr.push({
'id'
:_pu[i],
'text'
: _pr[i] });
}
//對密鑰重新進行排序
_newArr = _puArr.sort(PAR.compare(
"id"
));
for
(
var
i=0;i< _newArr.length; i++){
_code+=_newArr[i][
'text'
];
}
config.url = PAR.secret(config.url, _code,
true
);
})
|
我們分析分析,獲取接口地址返回pr和pu,然后組裝成puArr數組,對puArr通過PAR.compare方法按照id大小重新排序,
然后得到_code,然后通過PAR.secret方法,傳入頁面加密的url和_code和true
得到一個新的url,這里,我們大膽一點,新的url應該就是真實播放地址
<ignore_js_op>
ok,流程理清楚了,現在關鍵的就是PAR.compare和PAR.secret這2個函數了
我們全文模糊搜索PAR PAR:PAR= var PAR等關鍵字,很快就定位到尾號為676f.js里
<ignore_js_op>
五、OB混淆
我們不難發現,整個尾號為676f.js是一個混淆代碼,我們拷貝下來,分析分析
<ignore_js_op>
開頭定義了一個大數組,然后對這個大數組里的內容進行位移,再定義一個解密函數。后面大部分的值都調用了這個解密函數,以達到混淆的效果。
這個就是傳說中的OB混淆我們先看看PAR.compare和PAR.secret
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
'compare'
:
function
(_0x209a13) {
var
_0x2b0a5b = {
'hupwx'
:
function
(_0x28592c, _0x5c0aae) {
return
_0x28592c - _0x5c0aae;
}
};
return
function
(_0x4490c6, _0x47bde8) {
var
_0x11d139 = _0x4490c6[_0x209a13];
var
_0x2b383f = _0x47bde8[_0x209a13];
return
_0x2b0a5b[_0x15b4(
'6e'
,
'yYg5'
)](_0x11d139, _0x2b383f);
}
'secret'
:
function
(_0x141a71, _0x433196, _0x45cf51) {
var
_0x20b4ff = {
'bCQqZ'
: _0x15b4(
'6f'
,
'xfLW'
),
'hAcOs'
: _0x15b4(
'70'
,
'wHsA'
)
};
_0x433196 = CryptoJS[_0x15b4(
'71'
,
'p5&!'
)](_0x433196)[
'toString'
]();
var
_0x4f0cf6 = CryptoJS[
'enc'
][_0x15b4(
'72'
,
'a]Oa'
)][_0x15b4(
'73'
,
'Knq)'
)](_0x433196[
'substring'
](0x0, 0x10));
var
_0x1030c4 = CryptoJS[
'enc'
][_0x15b4(
'74'
,
'eKXt'
)][
'parse'
](_0x433196[
'substring'
](0x10));
if
(_0x45cf51) {
if
(_0x20b4ff[_0x15b4(
'75'
,
'eKXt'
)] !==
'vLPSI'
) {
return
CryptoJS[_0x15b4(
'76'
,
'AIBX'
)][_0x15b4(
'77'
,
'tzTY'
)](_0x141a71, _0x1030c4, {
'iv'
: _0x4f0cf6,
'padding'
: CryptoJS[_0x15b4(
'78'
,
'I0@('
)][
'Pkcs7'
]
})[
'toString'
](CryptoJS[
'enc'
][_0x15b4(
'79'
,
'vNOA'
)]);
}
else
{
PAR[_0x15b4(
'7a'
,
'wHsA'
)][
'post_r'
](a, b, c, d, _0x20b4ff[_0x15b4(
'7b'
,
'xB(L'
)]);
}
}
return
CryptoJS[_0x15b4(
'7c'
,
'bVZ)'
)][_0x15b4(
'7d'
,
'Bb3L'
)](_0x141a71, _0x1030c4, {
'iv'
: _0x4f0cf6,
'mode'
: CryptoJS[_0x15b4(
'7e'
,
'vzyU'
)][_0x15b4(
'7f'
,
'Ya)8'
)],
'padding'
: CryptoJS[_0x15b4(
'80'
,
'jX$g'
)][_0x15b4(
'81'
,
'xB(L'
)]
})[_0x15b4(
'82'
,
'OZnY'
)]();
}
|
看樣子混淆程度不高,一些關鍵字眼我們還是可以認出來,比如說CryptoJS,我們可以選擇動態調試硬鋼
不過,我還是選擇把它還原一下,本地慢慢分析。
我們剛講了,尾號為676f.js為標准的OB混淆樣式,解密函數很明顯就是_0x15b4,更何況PAR.compare和
PAR.secret多處調用,我們只需要把尾號為676f.js的前三段提取出來運行一遍,就能得到類似於_0x15b4('6e', 'yYg5')的值
這里可以選擇硬鋼替換,也可以選着用AST還原
<ignore_js_op>
不過需要注意的是這里有個暗坑,此段代碼會檢測你是否格式化,如果格式化了,就會內存爆破,進去無限循環,直到瀏覽器崩潰,有興趣的同學可以試試
<ignore_js_op>
<ignore_js_op>
簡單還原一下,然后PAR.compare和PAR.secret就成了這個樣子
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
'compare'
:
function
(_0x209a13) {
var
_0x2b0a5b = {
'hupwx'
:
function
(_0x28592c, _0x5c0aae) {
return
_0x28592c - _0x5c0aae;
}
};
return
function
(_0x4490c6, _0x47bde8) {
var
_0x11d139 = _0x4490c6[_0x209a13];
var
_0x2b383f = _0x47bde8[_0x209a13];
return
_0x2b0a5b[
"hupwx"
](_0x11d139, _0x2b383f);
};
},
'secret'
:
function
(_0x141a71, _0x433196, _0x45cf51) {
var
_0x20b4ff = {
'bCQqZ'
:
"\u8D4C\u535A\u8BC8\u9A97"
,
'hAcOs'
:
"LSEHx"
};
_0x433196 = CryptoJS[
"MD5"
](_0x433196)[
'toString'
]();
var
_0x4f0cf6 = CryptoJS[
'enc'
][
"Utf8"
][
"parse"
](_0x433196[
'substring'
](0x0, 0x10));
var
_0x1030c4 = CryptoJS[
'enc'
][
"Utf8"
][
'parse'
](_0x433196[
'substring'
](0x10));
if
(_0x45cf51) {
if
(
"LSEHx"
!==
'vLPSI'
) {
return
CryptoJS[
"AES"
][
"decrypt"
](_0x141a71, _0x1030c4, {
'iv'
: _0x4f0cf6,
'padding'
: CryptoJS[
"pad"
][
'Pkcs7'
]
})[
'toString'
](CryptoJS[
'enc'
][
"Utf8"
]);
}
else
{
PAR[
"danmu"
][
'post_r'
](a, b, c, d,
"\u8D4C\u535A\u8BC8\u9A97"
);
}
}
return
CryptoJS[
"AES"
][
"encrypt"
](_0x141a71, _0x1030c4, {
'iv'
: _0x4f0cf6,
'mode'
: CryptoJS[
"mode"
][
"CBC"
],
'padding'
: CryptoJS[
"pad"
][
"Pkcs7"
]
})[
"toString"
]();
},
|
先看PAR.compare,我們發現就是sort的排列方式函數,我們改寫一下
|
01
02
03
04
05
06
07
08
09
10
11
|
function
compare(d){
function
hupwx(a, b) {
return
a - b;
}
return
function
(a1, b1) {
var
a = a1[d];
var
b = b1[d];
return
hupwx(a, b);
};
}
|
至於PAR.secret,細心的童鞋已經發現他是調用加解密庫(crypto-js)進行的一個AES解密,我們也改寫一下
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
function
secret (url, _bcode) {
code = CryptoJS[
"MD5"
](_bcode)[
'toString'
]();
var
_0x4f0cf6 = CryptoJS[
'enc'
][
"Utf8"
][
"parse"
](code[
'substring'
](0x0, 0x10));
var
_0x1030c4 = CryptoJS[
'enc'
][
"Utf8"
][
'parse'
](code[
'substring'
](0x10));
return
CryptoJS[
"AES"
][
"decrypt"
](url, _0x1030c4, {
'iv'
: _0x4f0cf6,
'padding'
: CryptoJS[
"pad"
][
'Pkcs7'
]
})[
'toString'
](CryptoJS[
'enc'
][
"Utf8"
]);
}
|
幾個重要的參數'iv','padding',key清新可見
我們來梳理一下,前面我們說了對puArr通過PAR.compare方法重新排序,然后得到_code,然后傳入加密url和_code,
通過md5加密得到code,取code前16位得到IV,取后16位得到key,最后decrypt解密成明文url
因此我們改寫一下abc函數,全局搜索CryptoJS,拿到尾號為cd51.js,稍微改寫一下來驗證一下(當然這里直接調用crypto-js加解密庫也是一樣)
<ignore_js_op>
需要注意,每次請求接口地址url和pr/pu都要變化,為了方便我們暫時先固定一下
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
function
abc(){
//var _pr = $('meta[name="viewport"]').attr('id').replace('vod_','');
//var _pu = $('meta[charset="UTF-8"]').attr('id').replace('vod_','');
var
_pr =
"vod_dQb7shnQWf"
.replace(
'vod_'
,
''
);
var
_pu =
"vod_5318769240"
.replace(
'vod_'
,
''
);
var
_puArr = [];
var
_newArr = [];
var
_code =
''
;
var
url=
"a7GlmXWmVruOTAcF8VaB3idxA924haXMwKMPzu7dl0NDlQQVrqVTZwUvzFj8KRYjkthOjRXHwPtT86kzfj46WVnIA3DbSmBfQEE2ICd3OI2FBhOfcRauLNQ2kSxcrRlDOl8R6wY3q7tG6xBA0k
+Drs/iZFbc/llpLEyk06vOMkdt/3qxWxazor5E6mIvJ0PoieCvj9FJoooWECzXOiWgMBIX0EmKw5o4zWWRipLZTXdo69bbSFUJ1PphkS50snOkvdtzh1uKd2xTKqERLC
+iOTqRvBz8YtqjashI5MW0yhhkboK8StIK2QUIa0AACQBDJfhk3bxkmLyW8zdFBZa5iRfW5BNcq3DulkpKK8wY/+c40wesKygPXgBpgultR82yZmPCMHXrxDpxJDDywKXhYauH/iYyGO05V
+2pLMx6i9Biaq6XUPluh2VsMUe7GjWE1cKneEkmyJw4bMB23YY9Rr0RrV4gzoQ08xT+n2LeLsOKU4APkXuVEIKLXx+Wgo5SzpWqJL5N4MahNRpcetHavjoynwvCX1Mh59U4U67x5TKan8a9hO4209Qvc+0n
+fWxWjYZ0erlgTrxvlwU1EVQeRincA0jI0DF5tVtErYxs90="
;
for
(
var
i=0;i< _pu.length; i++){
_puArr.push({
'id'
:_pu[i],
'text'
: _pr[i] });
}
//對密鑰重新進行排序
_newArr = _puArr.sort(compare(
"id"
));
console.log(_newArr);
for
(
var
i=0;i< _newArr.length; i++){
_code+=_newArr[i][
'text'
];
}
url1 = secret(url, _code);
return
url1;
}
|
測試OK,至此播放地址拿到手了
<ignore_js_op>
以下附上js源碼和簡單的AST源碼,如果對AST感興趣,我們下次再聊
鏈接:https://pan.baidu.com/s/16ThFDjy9acw5eOH8LMaM5Q
提取碼:52pj
