先放一个地址:aHR0cHM6Ly93d3cubWlhb3F1bWgub3JnLw==
然后开始分析步骤
先随便找个漫画打开开发者工具看一下,这个图片都是有一个看着像加密的后缀,不是可以一般的找规律去请求的
我们看看图片的请求堆栈呢
直接跟进去打个断点
刷新一下浏览器
非常惊喜的发现所有图片的真实地址就在这里,我们看看作用域,这个newImgs是哪里来的
本地没有找到,是在全局作用域发现的,可以往上看看代码
发现就在他的上方,newImgs是通过decryptData函数传入DATA参数产生
var newImgs = decryptData(DATA)
下断点后重新刷新页面,发现DATA 是一个加密参数,decryptData是解密函数,我们跟进去看看decryptData是怎么实现的
是一段混淆代码,慢慢来瞅瞅啥玩意儿(大佬提醒了一下是魔改的ob混淆,修正一下,我选择用抠代码实现)
慢慢下断点看看
首先_0x4233是一个函数,也可以跟进去看一下,将_0x4233这个函数赋值给了_0x10c7de
_0x5caa61 是一个对象,里面有多个对象通过_0x10c7de函数实现值,同时包含多个自定义函数,可以简单读一下就会发现这个就是加密的核心
_0x5caa61对象中的内容是定值,我们继续分析后面的内容
继续下断调试,发现_0x5901db是一个数组, _0x8b2dee是一个字符串,_0xdb71bf是数字8,_0x2c8553 是调用对象中的函数FOitP 对需要解密的函数利用atob函数进行解码,_0x374bab是一个空字符串
接下来这一段代码就是加密的核心,我们可以看到很熟悉的_0x10c7de函数,_0x5caa61对象
for (let _0x1accd3 = 0x0; _0x1accd3 < _0x2c8553[_0x10c7de(0x16e, 'UBU0')]; _0x1accd3++) {
let _0xdfd4c = _0x5caa61[_0x10c7de(0x170, 'kvWz')](_0x1accd3, _0xdb71bf);
_0x374bab += String['fromCharCode'](_0x5caa61[_0x10c7de(0x17b, 'uC45')](_0x5caa61['NAbqC'](utf8_char_code_at, _0x2c8553, _0x1accd3), _0x5caa61[_0x10c7de(0x151, '))8u')](utf8_char_code_at, _0x8b2dee, _0xdfd4c)));
}
我们把断点继续放进去,然后替换掉定值,可以得到以下下代码:(昨天测试发现出了一点小问题,今天重新修改了以下,传入值应该是两个,一个加密数据,一个cid值,在_0x8b2dee那里,这个是变化的,不是定值,重新放上修改后的代码)
function decryptData(_0x515a62,cid) {
_0x5caa61 = {
'KgRhu': 'OC1iWGQ5aU4=',
'cgCzU': 'OC00Wlk1N1U=',
'KZOsE': 'OC02TU0yRWk=',
'OJFwP': 'OC01NFRpUXI=',
'Hnbwl': 'OC1QaDV4eDk=',
'kPQrm': "OC00Wlk1N1U=",
'qhdhc': function(_0x4b8d86, _0x170516) {
return _0x4b8d86 % _0x170516;
},
'FOitP': function(_0xcf1c19, _0x455a23) {
return _0xcf1c19(_0x455a23);
},
'viPVT': function(_0x277ac8, _0x2fa41e) {
return _0x277ac8 ^ _0x2fa41e;
},
'NAbqC': function(_0x430840, _0x554993, _0x537232) {
return _0x430840(_0x554993, _0x537232);
}
};
let _0x5901db = ["OC1iWGQ5aU4=", "OC1SWHlqcnk=", "OC1vWXZ3Vnk=", "OC00Wlk1N1U=", "OC1tYkpwVTc=", "OC02TU0yRWk=", "OC01NFRpUXI=", "OC1QaDV4eDk=", "OC1iWWdlUFI=", "OC1aOUEzYlc="]
, _0x8b2dee = atob(_0x5901db[_0x5caa61['qhdhc'](cid, 0xa)])
, _0xdb71bf = 8
, _0x2c8553 = _0x5caa61['FOitP'](atob, _0x515a62)
, _0x374bab = '';
for (let _0x1accd3 = 0x0; _0x1accd3 < _0x2c8553["length"]; _0x1accd3++) {
let _0xdfd4c = _0x5caa61["qhdhc"](_0x1accd3, _0xdb71bf);
_0x374bab += String['fromCharCode'](_0x5caa61["viPVT"](_0x5caa61['NAbqC'](utf8_char_code_at, _0x2c8553, _0x1accd3), _0x5caa61["NAbqC"](utf8_char_code_at, _0x8b2dee, _0xdfd4c)));
}
let _0x46d890 = atob(_0x374bab);
return JSON['parse'](_0x46d890);
}
然后运行会缺少utf8_char_code_at函数我们跟进去看看
非常熟悉的一个代码 _0x4233 赋值给_0x50b356,用_0x50b356函数进行处理,这样我们换掉定值扣下来这部分代码:
function utf8_char_code_at(_0x1f5ebe, _0x53220e) {
let _0x451daf = _0x1f5ebe["charAt"](_0x53220e);
return _0x451daf['charCodeAt'](0x0);
}
大致就是这些。放一个完整的测试:
function utf8_char_code_at(_0x1f5ebe, _0x53220e) {
let _0x451daf = _0x1f5ebe["charAt"](_0x53220e);
return _0x451daf['charCodeAt'](0x0);
}
function decryptData(_0x515a62,cid) {
_0x5caa61 = {
'KgRhu': 'OC1iWGQ5aU4=',
'cgCzU': 'OC00Wlk1N1U=',
'KZOsE': 'OC02TU0yRWk=',
'OJFwP': 'OC01NFRpUXI=',
'Hnbwl': 'OC1QaDV4eDk=',
'kPQrm': "OC00Wlk1N1U=",
'qhdhc': function(_0x4b8d86, _0x170516) {
return _0x4b8d86 % _0x170516;
},
'FOitP': function(_0xcf1c19, _0x455a23) {
return _0xcf1c19(_0x455a23);
},
'viPVT': function(_0x277ac8, _0x2fa41e) {
return _0x277ac8 ^ _0x2fa41e;
},
'NAbqC': function(_0x430840, _0x554993, _0x537232) {
return _0x430840(_0x554993, _0x537232);
}
};
let _0x5901db = ["OC1iWGQ5aU4=", "OC1SWHlqcnk=", "OC1vWXZ3Vnk=", "OC00Wlk1N1U=", "OC1tYkpwVTc=", "OC02TU0yRWk=", "OC01NFRpUXI=", "OC1QaDV4eDk=", "OC1iWWdlUFI=", "OC1aOUEzYlc="]
, _0x8b2dee = atob(_0x5901db[_0x5caa61['qhdhc'](cid, 0xa)])
, _0xdb71bf = 8
, _0x2c8553 = _0x5caa61['FOitP'](atob, _0x515a62)
, _0x374bab = '';
for (let _0x1accd3 = 0x0; _0x1accd3 < _0x2c8553["length"]; _0x1accd3++) {
let _0xdfd4c = _0x5caa61["qhdhc"](_0x1accd3, _0xdb71bf);
_0x374bab += String['fromCharCode'](_0x5caa61["viPVT"](_0x5caa61['NAbqC'](utf8_char_code_at, _0x2c8553, _0x1accd3), _0x5caa61["NAbqC"](utf8_char_code_at, _0x8b2dee, _0xdfd4c)));
}
let _0x46d890 = atob(_0x374bab);
return JSON['parse'](_0x46d890);
}
key='bx5GXTU+ABt3RHxMGz06RnV5ZAUaLQQFdkd0XRgqG0NbQEJdGwAbHVxlZ0M3EyERdBxCQjcEBwh0Q11EDT5oCll6AE0OOmQYWh8EVxhbPQZhelFYN1gmBGEfDEA1Ph8RdFdjVxgTNkN3aVlXGBMUQndpcAUaLRALdnlRZwZbJQNse11FNVoDJmxqe0QGPRMLdEBFQw4QG0t0ZUZdNT4AG3dEfEwbPTpGdXlkBRotBAV2R3BdGCobQ1tAQl0bABsdXGVnQzcTIRF0HEJCNwQHCHRDXUQNPmgKWXoATQ46ZBhaHwRXGFs9BmF6UVg3WCYEYR8MQDU+HxF0V2NXGBM2Q3dpWVcYExRCd2lwBRotEAt2R3N3NRM5Q1p6fAQaEws3d2VvZgAEAyh0QEVDDhAbS3RlRl01PgAbd0R8TBs9OkZ1eWQFGi0EBXZHfF0YKhtDW0BCXRsAGx1cZWdDNxMhEXQcQkI3BAcIdENdRA0+aApZegBNDjpkGFofBFcYWz0GYXpRWDdYJgRhHwxANT4fEXRXY1cYEzZDd2lZVxgTFEJ3aXAFGi0QC3ZHZ3AaPRs5anlZZQctNShaH29DAy8XP3RARUMOEBtLdGVGXTU+ABt3RHxMGz06RnV5ZAUaLQQFdkd4XRgqG0NbQEJdGwAbHVxlZ0M3EyERdBxCQjcEBwh0Q11EDT5oCll6AE0OOmQYWh8EVxhbPQZhelFYN1gmBGEfDEA1Ph8RdFdjVxgTNkN3aVlXGBMUQndpcAUaLRALdkdRTDU9A0FhRkVtAgQpGHYeRUwHBRNEdEBFQw4QG0t0ZUZdNT4AG3dEfEwbPTpGdXlkBRotBAV2R2RdGCobQ1tAQl0bABsdXGVnQzcTIRF0HEJCNwQHCHRDXUQNPmgKWXoATQ46ZBhaHwRXGFs9BmF6UVg3WCYEYR8MQDU+HxF0V2NXGBM2Q3dpWVcYExRCd2lwBRotEAt2V3QADiE+Q2J4UXE3Awsjd2tvRDEFNTF0QEVDDhAbS3RlRl01PgAbd0R8TBs9OkZ1eWQFGi0EBXZHYF0YKhtDW0BCXRsAGx1cZWdDNxMhEXQcQkI3BAcIdENdRA0+aApZegBNDjpkGFofBFcYWz0GYXpRWDdYJgRhHwxANT4fEXRXY1cYEzZDd2lZVxgTFEJ3aXAFGi0QC3ZXZ2IGWGgUYXtFYAA9NQR2eHt4BwU1CnRARUMOEBtLdGVGXTU+ABt3RHxMGz06RnV5ZAUaLQQFdkdsXRgqG0NbQEJdGwAbHVxlZ0M3EyERdBxCQjcEBwh0Q11EDT5oCll6AE0OOmQYWh8EVxhbPQZhelFYN1gmBGEfDEA1Ph8RdFdjVxgTNkN3aVlXGBMUQndpcAUaLRALdldvQjBbCyNsHHN/GlobOXZuBGcBPyFGdEBFQw4QG0t0ZUZdNT4AG3dEfEwbPTpGdXlkBRotBAV2R1ZdGCobQ1tAQl0bABsdXGVnQzcTIRF0HEJCNwQHCHRDXUQNPmgKWXoATQ46ZBhaHwRXGFs9BmF6UVg3WCYEYR8MQDU+HxF0V2NXGBM2Q3dpWVcYExRCd2lwBRotEAt3aXdmBVghJFpGe2IZBwcebnVRTgI8ZCZ0QEVDDhAbS3RlRl01PgAbd0R8TBs9OkZ1eWQFGi0EBXZHUl0YKhtDW0BCXRsAGx1cZWdDNxMhEXQcQkI3BAcIdENdRA0+aApZegBNDjpkGFofBFcYWz0GYXpRWDdYJgRhHwxANT4fEXRXY1cYEzZDd2lZVxgTFEJ3aXAFGi0QC3dpeEMNWyUYYR1vdgAuFzZrem9hNi4fBHRARUMOEBtLdGVGXTU+ABt3RHxMGz06RnV5ZAUaLQQFdkdeXRgqG0NbQEJdGwAbHVxlZ0M3EyERdBxCQjcEBwh0Q11EDT5oCll6AE0OOmQYWh8EVxhbPQZhelFYN1gmBGEfDEA1Ph8RdFdjVxgTNkN3aVlXGBMUQndpcAUaLRALd2lvdwFZKTVgHFF2Gi8cQHdqY38aPRAIdEBFQw4QG0t0ZUZdNT4AG3dEfEwbPTpGdXlkBRotBAV2R3BDHQAmG1x1f0cdAz4bWWVnBDchHERgbgxXGFobHltUAAA1PhcEW3pZQTcEBAdhHwxADCpoAlp6c1oOMR8RdB97QjY+PRhgbg0FDCppRnZ5UgEMKmkKdmlSTBo9AAV1R1IBMD0LQWJ7YAYDLAMnW3pRAAIvCzRrVABFNy4yG15+TQMdBD0ZcUdaXRk9Okd3aXAEGj0AQ3VpbEwZOhgBcUNjTTYqGERxQF0EMCETCHdBQkIMKmgLYnV4QTEuPRpaHnNENgcbHnRAe0I2PyYEWXoEXA5bBwhgbgxeNltgAmEcQkIaPyYEd2lgABs/JgR1eWQAGT0EQnVpfAEZAz0HXXhNRQFaEypqR3hAGltgI2wdRkE1BxMccUMFRzEQGwJibnwCHQMUR3d5UkwaLQRCdnl0Bhk9GBt0bn8FNwQmG3dEf1swIQMFW1dFVxhYJgRbQGNOGAc5AmF6DEw1PmQLYn4AXjZbYBF0H1lADT41HlscQkINW2gGWXp7VxgTBxF0V1IFGy09EXRXcAQbLRRDdml0TRs9C0RcR38FAAc5JGwcYAYZAgs+WntZeBgEIQViVH8NGCEiG1l6ZF0bABgKd3leABk9AEN2aWBDGgMUCHFEQl0wMRsBcUdaXTUhA0JbZXgCDCpoEXQef1g3EGRGWXpzQjc+PQdbQGBBDVtoBmBuDEQ2PhccYnV7VxhbHwRaelleDCppQ2BuDQAaPTZHYG4NTBotNgp2eWRDGQM6R1t4DGcOPQRAYRxzTBlYBAVbR11dGDpkA1tqVl0yOilFcUBZXx0DPht1eV4BGy0UQnZ5ZAUZLQgKdm58Rx0HBwtabnwCHQQ5Qlxld04bBSYEYG4MTQ4xHAddallcNloXAlpDf1gYBB8EWntCQjU+YBpiH2NODCpoGFofBEQNWCYEdntCQhstBEZ3e0JCGT0ARnV5YAQZLRwFdUBjAhouZARaeEJAAi8cQlsfTW4BWDIHWUN3Wh0HYQFdVH9EDioYRHFHcAEbPTYKdmlgBBo9EEB1eWBdGCobQ1tAQl0bABsdXGVnQzcTIRF0HEJCNwQHCHRDXUQNPmgKWXoATQ46ZBhaHwRXGFs9BmF6UVg3WCYEYR8MQDU+HxF0V2NXGBM2Q3dpWVcYExRCd2lwBRotEAh1aVFfGzEHC1llfAAaBBtCbB97ZgBZA0F0QEVDDhAbS3RlRl01PgAbd0R8TBs9OkZ1eWQFGi0EBXZHcAYdACYbXHV/Rx0DPhtZZWcENyEcRGBuDFcYWhseW1QAADU+FwRbellBNwQEB2EfDEAMKmgCWnpzWg4xHxF0H3tCNj49GGBuDQUMKmlGdnlSAQwqaQp2aVJMGj0ABXVXcEwHAggKXEBRYRksMgZaHllzATwHKmJUAEU3LjIbXn5NAx0EPRlxR1pdGT06R3dpcAQaPQBDdWlsTBoQGAFxQ2NNNioYRHFAXQQwIRMId0FCQgwqaAtidXhBMS49Gloec0Q2BxsedEB7QjY/JgRZegRcDlsHCGBuDF42W2ACYRxCQho/JgR3aWAAGz8mBHV5ZAAZPQRCdWl4TBotHzRbentxAiE+CGJBUUwABTk1WhxeQTUHExxxQwVHMRAbAmJufAIdAxRHd3lSTBotBEJ2eXQGGT02G3RufwU3BCYbd0R/WzAhAwVbV0VXGFgmBFtAY04YBzkCYXoMTDU+ZAtifgBeNltgEXQfWUANPjUeWxxCQg1baAZZentXGBMHEXRXUgUbLT0RdFdwBBstFEN2aXROGT05Bllrb2IGPmABdnlvZQ4+ZQVae294GAQhBWJUfw0YISIbWXpkXRsAGAp3eV4AGT0AQ3ZpYEMaAxRHcURCXTAxGwFxR1pdNSEDQltleAIMKmgRdB5/WDcQZEZZenNCNz49B1tAYEENW2gGYG4MRDY+FxxidXtXGFsfBFp6WV4MKmlDYG4NABo9Nkdgbg1MGi02CnZ5ZEMZExgKYUFvfjVbNRpsdW93AD01JVtoWUQ2EGQDW2pWXTI6KUVxQFlfHQM+G3V5XgEbLRRCdnlkBRktCAt1bnxHHQcHC1pufAIdBDlCXGV3ThsFJgRgbgxNDjEcB11qWVw2WhcCWkN/WBgEHwRae0JCNT5gGmIfY04MKmgYWh8ERA1YJgR2e0JCGy0ERnd7QkIZPQBGdXlgBBktHAt2eHNsNlklHmkdDEUZPgcYb0YAXgFaFAdZQ3daHQdhAV1Uf0QOKhhEcUdwARs9Ngp2aWAEGj0QQHVHcF0YKhtDW0BCXRsAGx1cZWdDNxMhEXQcQkI3BAcIdENdRA0+aApZegBNDjpkGFofBFcYWz0GYXpRWDdYJgRhHwxANT4fEXRXY1cYEzZDd2lZVxgTFEJ3aXAFGi0QCHVHXUIGPmlAdWtZcAI8YAZraWwGAgJkCHRARUMOEBtLYHwICQ=='
cid=54506
console.log(decryptData(key,cid))
同时给一个之前做的一个多线程漫画爬虫
import requests
import time
from bs4 import BeautifulSoup
from concurrent.futures import ThreadPoolExecutor
import os
import re
def download_picture(url, count, title):
print(f'----->{title}开始下载<---------')
res = requests.get(url, headers=headers).content
time.sleep(1)
save_picture(res, count, title)
def get_mic_url(url, batch):
print(f'----->第{batch}批漫画下载开始<---------')
for n in url:
title = dic_all.get(n)
r = requests.get(n, headers=headers)
r.encoding = 'utf-8'
pic_url = re.search('\[(\".*?\")]', r.text)[0].replace('\\/', '/').replace('["', '').replace('"]', '')
img_list = [img for img in pic_url.split('","')]
for j in range(0, len(img_list)):
img_url = 'https://img.wubizigeng.com' + img_list[j]
download_picture(img_url, j, title)
time.sleep(1)
def get_main_url():
print('----->开始获取漫画链接<---------')
title_dic = {}
response = requests.get('https://www.doupobook.cc/manhua/doupocangqiong/', headers=headers)
response.encoding = 'utf-8'
soup = BeautifulSoup(response.text, 'lxml')
url_list = soup.select('#play_0 ul li>a')
for i in url_list:
url = 'https://doupo.935666.xyz' + i.get('href')
title = i.text
title_dic[url] = title
print('----->漫画链接获取完成<---------')
return title_dic
def save_picture(content, count, title):
if not os.path.exists(f'斗破漫画'):
os.mkdir(f'斗破漫画')
if not os.path.exists(f'斗破漫画/{title}'):
os.mkdir(f'斗破漫画/{title}')
with open(f'斗破漫画/{title}/{count}.jpg', 'wb') as f:
f.write(content)
print(f'{title} 第{count}张图片下载完成')
if __name__ == '__main__':
headers = {
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'cache-control': 'no-cache',
'cookie': 'cf_clearance=QrF5Eo.rgZOPiNUtdh7GOBuLyKC6eAsmVLjeYTDS84E-1729251907-1.2.1.1-1JPwAVQpOnlT9nTikimyhEKcDu32_154rNF7I26prGZj17gtQlljCiewOiTY0WzSfSnpt4vOHHGk3VvJJF7FIHGHDGShuX0hIrlSGzsp.oEblSAcVgRoFEbh8FKwZOzZvWlluz1rXPaLSVUlXSbfXPLcc0rOvHSl_ZGPMFe3X0_lR_9Knd6qL9Jjfh0FjN16cNX_SiIQUzvba3rajvXAbfgEVBSjLNemdEDRvmQGfu.8NMfpJDVuzo3hoB1BoI_z4KFtBFuMyJ62R8x62Rnq.NBt.osZLOR.2ObdbRZhQtTa2T25p1S4.v4NDaTre2AGGe8_e2fPY0K6y4vauHxBoAVuJnQ8ksZNQ18RZZlO_Gdd1.F_NdFnQNpO8WHhnneVnkmIL8l5GWIo7bCWmJDVrw',
'pragma': 'no-cache',
'priority': 'u=0, i',
'^sec-ch-ua': '^\\^Microsoft',
'sec-ch-ua-mobile': '?0',
'^sec-ch-ua-platform': '^\\^Windows^\\^^',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'none',
'sec-fetch-user': '?1',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0',
}
dic_all = get_main_url()
total = len(dic_all)
MAX_WORKERS = 20
remained = total % MAX_WORKERS
batch = (total / MAX_WORKERS)
batch = int(batch if remained == 0 else batch + 1)
print(f'==> total: {total} workers: {MAX_WORKERS} count: {batch}')
urls = [i for i in dic_all.keys()]
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as t:
for i in range(0, batch):
batchUrls = urls[i * MAX_WORKERS:(i + 1) * MAX_WORKERS]
t.submit(get_mic_url, batchUrls, i)
没有回复内容