原貼地址:手撕某解析網站,獲取真實播放地址(保姆級講解) - 『脫殼破解區』 - 吾愛破解 - 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