177 lines
5.1 KiB
JavaScript
177 lines
5.1 KiB
JavaScript
// # Source: https://github.com/huwangkeji/juejin_checkin/blob/main/juejin-extension/content.js
|
||
// # Raw: https://raw.githubusercontent.com/huwangkeji/juejin_checkin/main/juejin-extension/content.js
|
||
// # Repo: huwangkeji/juejin_checkin
|
||
// # Path: juejin-extension/content.js
|
||
// # UploadedAt: 2026-05-19T14:34:11Z
|
||
// # SHA256: 45c4e3523ff88ee080e76c2c8b635c4a14657f9f97fa395d368b9bccf09b4bca
|
||
// # Category: APP版/抓包
|
||
// # Evidence: cookie/token/authorization/header
|
||
//
|
||
|
||
/**
|
||
* 掘金参数提取器 - Content Script (MAIN world)
|
||
* 直接在页面主世界运行,hook 真实的 fetch/XHR,参数存到 sessionStorage
|
||
*/
|
||
|
||
(function() {
|
||
'use strict';
|
||
|
||
if (window.__JUEJIN_EXTRACTOR_HOOKED__) return;
|
||
window.__JUEJIN_EXTRACTOR_HOOKED__ = true;
|
||
|
||
const STORAGE_KEY = '__juejin_extractor_data__';
|
||
|
||
function readStored() {
|
||
try {
|
||
return JSON.parse(sessionStorage.getItem(STORAGE_KEY) || '{}');
|
||
} catch (e) {
|
||
return {};
|
||
}
|
||
}
|
||
|
||
function writeStored(data) {
|
||
const existing = readStored();
|
||
const merged = { ...existing, ...data };
|
||
try {
|
||
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(merged));
|
||
} catch (e) {}
|
||
}
|
||
|
||
function extractFromUrl(url) {
|
||
try {
|
||
const u = new URL(url);
|
||
if (u.hostname === 'api.juejin.cn' || u.hostname.includes('juejin.cn')) {
|
||
return {
|
||
aid: u.searchParams.get('aid') || '',
|
||
uuid: u.searchParams.get('uuid') || '',
|
||
msToken: u.searchParams.get('msToken') || '',
|
||
a_bogus: u.searchParams.get('a_bogus') || '',
|
||
spider: u.searchParams.get('spider') || '',
|
||
};
|
||
}
|
||
} catch (e) {}
|
||
return null;
|
||
}
|
||
|
||
function mergeParams(params, cookie) {
|
||
const data = readStored();
|
||
let changed = false;
|
||
|
||
if (cookie && cookie.length > 10) {
|
||
data.cookie = cookie;
|
||
changed = true;
|
||
}
|
||
if (params.aid && params.aid !== 'null' && params.aid !== data.aid) {
|
||
data.aid = params.aid;
|
||
changed = true;
|
||
}
|
||
if (params.uuid && params.uuid.length > 5 && params.uuid !== data.uuid) {
|
||
data.uuid = params.uuid;
|
||
changed = true;
|
||
}
|
||
if (params.msToken && params.msToken.length > 5 && params.msToken !== data.msToken) {
|
||
data.msToken = params.msToken;
|
||
changed = true;
|
||
}
|
||
if (params.a_bogus && params.a_bogus.length > 5 && params.a_bogus !== data.a_bogus) {
|
||
data.a_bogus = params.a_bogus;
|
||
changed = true;
|
||
}
|
||
if (params.spider && params.spider !== data.spider) {
|
||
data.spider = params.spider;
|
||
changed = true;
|
||
}
|
||
|
||
if (changed) {
|
||
data.captureTime = new Date().toLocaleString('zh-CN');
|
||
writeStored(data);
|
||
}
|
||
}
|
||
|
||
// ===== Hook fetch =====
|
||
const _origFetch = window.fetch;
|
||
window.fetch = function fetchHook(input, init) {
|
||
try {
|
||
const url = typeof input === 'string' ? input : (input.url || input.toString());
|
||
const params = extractFromUrl(url);
|
||
if (params) {
|
||
let cookie = '';
|
||
try { cookie = document.cookie; } catch (e) {}
|
||
mergeParams(params, cookie);
|
||
}
|
||
} catch (e) {}
|
||
return _origFetch.apply(this, arguments);
|
||
};
|
||
|
||
// ===== Hook XMLHttpRequest =====
|
||
const _origOpen = XMLHttpRequest.prototype.open;
|
||
const _origSend = XMLHttpRequest.prototype.send;
|
||
|
||
XMLHttpRequest.prototype.open = function(method, url) {
|
||
this._juejinUrl = url;
|
||
return _origOpen.apply(this, arguments);
|
||
};
|
||
|
||
XMLHttpRequest.prototype.send = function(body) {
|
||
try {
|
||
if (this._juejinUrl) {
|
||
const params = extractFromUrl(this._juejinUrl);
|
||
if (params) {
|
||
let cookie = '';
|
||
try { cookie = document.cookie; } catch (e) {}
|
||
mergeParams(params, cookie);
|
||
}
|
||
}
|
||
} catch (e) {}
|
||
return _origSend.apply(this, arguments);
|
||
};
|
||
|
||
// ===== 扫描 performance entries =====
|
||
function scanPerf() {
|
||
try {
|
||
const entries = performance.getEntriesByType('resource');
|
||
for (const entry of entries) {
|
||
if (entry.name && entry.name.includes('api.juejin.cn')) {
|
||
const params = extractFromUrl(entry.name);
|
||
if (params) {
|
||
let cookie = '';
|
||
try { cookie = document.cookie; } catch (e) {}
|
||
mergeParams(params, cookie);
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {}
|
||
}
|
||
|
||
scanPerf();
|
||
let count = 0;
|
||
const timer = setInterval(() => {
|
||
scanPerf();
|
||
count++;
|
||
if (count >= 20) clearInterval(timer);
|
||
}, 2000);
|
||
|
||
// ===== 页面稳定后主动触发 harmless 请求 =====
|
||
function triggerCapture() {
|
||
try {
|
||
const t = Date.now();
|
||
_origFetch.call(window,
|
||
`https://api.juejin.cn/growth_api/v1/get_today_status?aid=2608&t=${t}`,
|
||
{ credentials: 'include', headers: { 'Accept': 'application/json' } }
|
||
).catch(() => {});
|
||
} catch (e) {}
|
||
}
|
||
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
setTimeout(triggerCapture, 1000);
|
||
setTimeout(triggerCapture, 3000);
|
||
});
|
||
} else {
|
||
setTimeout(triggerCapture, 1000);
|
||
setTimeout(triggerCapture, 3000);
|
||
}
|
||
|
||
console.log('[掘金参数提取器] Content script (MAIN world) 已注入');
|
||
})();
|