DuFile网盘逆向下载链接免等待破解思路
最近在逛某論壇時(shí)碰到了DuFile網(wǎng)盤(pán)的資源,對(duì)于這列靠下載限速+繁瑣跳轉(zhuǎn)促使用戶付費(fèi)的收費(fèi)盤(pán),當(dāng)然要破解一下玩玩。經(jīng)過(guò)幾個(gè)小時(shí)的分析,筆者發(fā)現(xiàn)這家網(wǎng)盤(pán)的策略是比較有趣的,故此記錄一下。
注意:此類(lèi)收費(fèi)盤(pán),破解會(huì)員高速通道在理論上基本是不可能的,我們能做的只是規(guī)避掉等待時(shí)間。
打開(kāi)鏈接,http://dufile.com/file/xxx2e80bxxxx00da.html(地址已做處理),能看出網(wǎng)站的模板都是那種收費(fèi)網(wǎng)盤(pán)的大路貨,直接開(kāi)調(diào)試模式抓包,但這里就遇到了第一個(gè)坑。
網(wǎng)頁(yè)直接進(jìn)入無(wú)法點(diǎn)擊的調(diào)試狀態(tài),只要不關(guān)閉調(diào)試窗口就一直這樣,而且事后證明如果在訪問(wèn)網(wǎng)址前就開(kāi)啟調(diào)試面板則網(wǎng)頁(yè)根本就不會(huì)加載。這實(shí)際是一種不太常見(jiàn)的前端反逆向措施(常見(jiàn)的是js代碼混淆),詳情可見(jiàn)這篇文章,破解方法見(jiàn)此。
破解后繼續(xù)在調(diào)試工具里查看,下一個(gè)跳轉(zhuǎn)的頁(yè)面地址是把/file/換成/down/,這也是此類(lèi)網(wǎng)盤(pán)地址跳轉(zhuǎn)的一概路子,新頁(yè)面里我們輸入驗(yàn)證碼,追蹤到下載按鈕綁定的函數(shù),發(fā)現(xiàn)它的代碼在http://dufile.com/js/file.js里,這里的代碼根本沒(méi)法讀,不過(guò)它用的是最簡(jiǎn)單的編碼加密,在線解密一下就明晰了很多;為了增加逆向難度,前端對(duì)變量名也做了隨機(jī)化處理。我們關(guān)注第一行的var _0x100e列表變量,里面出現(xiàn)了debug,console等字樣,可以猜想它們?cè)诮?jīng)過(guò)某些處理后形成的語(yǔ)句就會(huì)對(duì)常用的前端調(diào)試手段產(chǎn)生干擾。
幸運(yùn)的是函數(shù)名沒(méi)有加密,我們找到down_file函數(shù)(其余函數(shù)都是舉報(bào),開(kāi)vip以及無(wú)關(guān)緊要的xxx),然后用查找功能追蹤里面涉及到的變量,發(fā)現(xiàn)引用關(guān)系很簡(jiǎn)單,只涉及一小段代碼
var _0x100e = ['<divfrmmask"></div>' + _0x5289('0x67') + _0x5289('0x68') + _0x5289('0x69') + '<iframe id="show_down" frameborder="0" scrolling="no" width="352" height="316" scrolling="auto" src="about:blank"></iframe></div></div>';
document[_0x5289('0x19')][_0x5289('0x6a')](_0x385fa2);
showMask(!![], 0x15a, 0x164);
document[_0x5289('0x10')](_0x5289('0x6b'))['src'] = _0x5289('0x6c') + _0x5876ce + _0x5289('0x6d') + _0x3d630b;
}
View Code
將變量名作替換后就很明了
var elelist = ['<divfrmmask"></div>' + _0x5289('0x67') + _0x5289('0x68') + _0x5289('0x69') + '<iframe id="show_down" frameborder="0" scrolling="no" width="352" height="316" scrolling="auto" src="about:blank"></iframe></div></div>';
document[_0x5289('0x19')][_0x5289('0x6a')](_0x385fa2);
showMask(!![], 0x15a, 0x164);
*/
document[getelefunc('0x10')](getelefunc('0x6b'))['src'] = getelefunc('0x6c') + fileid + getelefunc('0x6d') +'0';
//關(guān)鍵在于getelefunc('0x6c') + fileid + getelefunc('0x6d'),分別對(duì)應(yīng)elelist[108]和elelist[109],一查便知。
}
View Code
指向了一個(gè)鏈接:http://dufile.com/dd.php?file_key=xxxxxxxxxxxx&p=0,而此鏈接GET返回的內(nèi)容里就含有直接下載地址。
事實(shí)上更簡(jiǎn)單的方法是直接用第三方工具抓包,比如Charles
可以很直觀的看出發(fā)包次序和包內(nèi)容,于是構(gòu)造Python腳本如下:
import requests
from bs4 import BeautifulSoup as Bs
import re
class DuFile(object):
def __init__(self):
self.headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763"}
def getfile(self, url, filepath):
session = requests.session() #用session來(lái)維持訪問(wèn)中的cookie
req1 = session.get(url, headers=self.headers)
if req1.status_code==200:
content = Bs(req1.text, 'lxml')
file_size = content.select_one('#fbody1').select_one('b').text
filename = content.select_one('.title').text
filename = filename.split("下載")[1].strip()
print("filename: %s, filesize: %sKB" %(filename, file_size))
else:
return 'error'
targetaddr = url.replace('/file/','/down/')
self.headers.update({'Host':'dufile.com', "Referer":url})
req2 = session.get(targetaddr, headers=self.headers)
file_id = re.search(r'/down/([a-z0-9]+)', targetaddr).group(1)
self.headers.update({"Referer":targetaddr})
req4 = session.get("http://dufile.com/dd.php?file_key={}&p=0".format(file_id), headers=self.headers)
downurl = re.search(r"id="downs" href="(http.+?)"", req4.text).group(1)
host = re.search(r'http://(.+?)/down', downurl).group(1)
req_file = session.get(downurl, headers={'Host':host, 'Referer':"http://dufile.com/dd.php?file_key={}&p=0".format(file_id)})
with open(filepath+filename, 'wb') as f:
f.write(req_file.content)
return filename
View Code
但使用中直接拋出了異常,req4返回了意外的結(jié)果,是一個(gè)地址,打開(kāi)后
被認(rèn)出來(lái)了......那么疏漏在哪里了呢?
回顧之前的抓包,腳本比正常訪問(wèn)少的只有
兩處。2發(fā)包時(shí)間在問(wèn)題點(diǎn)的后面,應(yīng)該不是,那就只有1,1是驗(yàn)證碼的相關(guān)操作,分為一個(gè)get圖片請(qǐng)求和一個(gè)post輸入值提交,如果輸入值正確即返回1。因?yàn)榻^大多數(shù)網(wǎng)盤(pán)(飛貓、訊牛等)只會(huì)用這個(gè)返回值來(lái)決定是否執(zhí)行下一步的下載函數(shù),所以根本不需要去真的填寫(xiě)驗(yàn)證碼(飛貓?jiān)圃幸欢螘r(shí)間把真正地址寫(xiě)在返回值里,這種是沒(méi)辦法跳過(guò)去的),DuFile是否在這里也做了驗(yàn)證?
經(jīng)試驗(yàn),DuFile的驗(yàn)證碼圖片分為兩種,分別是4位純數(shù)字和計(jì)算加減法,受URL里的down_code加不加下劃線控制,因?yàn)槲覀儾皇歉鉉V的,所以沒(méi)必要花時(shí)間研究驗(yàn)證碼識(shí)別問(wèn)題,直接調(diào)第三方服務(wù)就好,打碼服務(wù)百度一下就能找到,一個(gè)碼一般1分到幾分錢(qián),還是很便宜的,如果是那種比較白給的驗(yàn)證碼,甚至可以試試百度免費(fèi)的圖片轉(zhuǎn)文字服務(wù),這里就不多做介紹了。直接上修改版代碼
import requests
from bs4 import BeautifulSoup as Bs
import re
import random
import base64
import time
import json
import hashlib
def md5(ss):
fd = hashlib.md5()
fd.update(ss.encode('utf-8'))
return fd.hexdigest()
def veryimg(img_data):
link = "http://xxxx.xxxx.com/api/capreg" #這里就不放具體是哪家了
timestamp = str(int(time.time()))
pd_id = YOUR_ID
pd_key = YOUR_KEY
sign = md5(pd_id + timestamp + md5( timestamp + pd_key))
predict_type = '10400' #4位純數(shù)字
data = {
'user_id':pd_id,
'timestamp':timestamp,
'sign':sign,
'predict_type':predict_type,
'img_data':img_data
}
req = requests.post(link, data)
result = json.loads(req.text)['RspData']
code = json.loads(result)['result']
return code
class DuFile(object):
def __init__(self):
self.headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763"}
def getfile(self, url, filepath):
session = requests.session() #用session來(lái)維持訪問(wèn)中的cookie
req1 = session.get(url, headers=self.headers)
if req1.status_code==200:
content = Bs(req1.text, 'lxml')
file_size = content.select_one('#fbody1').select_one('b').text
filename = content.select_one('.title').text
filename = filename.split("下載")[1].strip()
print("filename: %s, filesize: %sKB" %(filename, file_size))
else:
return 'error'
targetaddr = url.replace('/file/','/down/')
self.headers.update({'Host':'dufile.com', "Referer":url})
req2 = session.get(targetaddr, headers=self.headers)
file_id = re.search(r'/down/([a-z0-9]+)', targetaddr).group(1)
self.headers.update({"Referer":targetaddr})
'''
新增驗(yàn)證碼提交
'''
req3 = session.get("http://dufile.com/downcode.php?{}".format(str(random.random())), headers=self.headers)
# 這里的隨機(jī)值是防止瀏覽器去讀緩存而設(shè)的,我們腳本里其實(shí)不加也行
if req3.status_code==200:
# 將圖片二進(jìn)制轉(zhuǎn)為base64字符串傳入接口
code = veryimg(base64.b64encode(req3.content).decode())
else:
return 'error'
req_p = session.post("http://dufile.com/downcode.php", data={'action':'yz','id':file_id, 'code':code}, headers=self.headers)
#
req4 = session.get("http://dufile.com/dd.php?file_key={}&p=0".format(file_id), headers=self.headers)
downurl = re.search(r"id="downs" href="(http.+?)"", req4.text).group(1)
host = re.search(r'http://(.+?)/down', downurl).group(1)
req_file = session.get(downurl, headers={'Host':host, 'Referer':"http://dufile.com/dd.php?file_key={}&p=0".format(file_id)})
# 直接一次性請(qǐng)求,如果文件比較大建議用分塊下載(不要用多線程,免費(fèi)用戶不支持的),寫(xiě)法網(wǎng)上教程很多這里不贅述
with open(filepath+filename, 'wb') as f:
f.write(req_file.content)
return filename
View Code
重新運(yùn)行,搞定!
總結(jié)
筆者對(duì)Yunfile, 飛貓?jiān)疲屎缭疲嵟#峭ǎ珺usdisk等多家收費(fèi)盤(pán)均進(jìn)行過(guò)逆向破解,DuFile不是難度最大的一個(gè),但它的反破解套路比較有趣,一方面用到了禁調(diào)試腳本,一方面又細(xì)心地在驗(yàn)證碼上下了功夫,這兩種在筆者看來(lái)都有些劍走偏鋒的意思;特別是驗(yàn)證碼的部分,誤導(dǎo)性很強(qiáng),如果配合上彩虹云用的js修改cookies驗(yàn)證,應(yīng)該能攔住一大波人。而且經(jīng)筆者測(cè)試,DuFile的下載間隔限制是IP級(jí)的,所以即使每次的session都是新生成,也得老老實(shí)實(shí)等上10分鐘,或者上IP代理池,這點(diǎn)許多網(wǎng)盤(pán)也沒(méi)能做到。
真有下載剛需的話(嘿嘿嘿~你懂的),還是建議充個(gè)會(huì)員,中國(guó)的服務(wù)器、帶寬真心挺貴的,人家也要恰飯嘛。
總結(jié)
以上是生活随笔為你收集整理的DuFile网盘逆向下载链接免等待破解思路的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2张信用卡怎么排账单日?做法其实很简单
- 下一篇: (12)odoo各种提前期和时间