该项目是对开源项目进行二开,改写的hook脚本,新增了cookie添加形式的捕获,若有侵权,请联系博主删除。
下面贴运行截图:
解释: 绿色: 新增cookie
红色 : 删除cookie
黄色 : cookie更改
可以根据cookie状态跟踪目标cookie进行分析。
const debuggerRules = [{"add|update": "epssw"}];
这里是debugger的规则,
支持以下几种格式:
"add": true, "update": true, "delete": true, "read": true,
形式为规则:cookie名,会在触发的时候下断。
下附代码:
// ==UserScript==
// @name JS-Cooker
// @description 用于监控js对cookie的修改,或者在cookie符合给定条件时进入断点
// @author allenyang
// @match *://*/*
// @run-at document-start
// @grant none
// ==/UserScript==
(() => {
const debuggerRules = [{"add|update": "epssw"}];
const enableEventDebugger = {
"add": true, "update": true, "delete": true, "read": true,
}
const consoleLogFontSize = 12;
const ignoreUpdateButNotChanged = false;
let realDocumentCookieProperty = null;
const definePropertyIsMe = "allenyang-js-hooker";
(function () {
Object.defineProperty = new Proxy(Object.defineProperty, {
apply: function (target, thisArg, argArray) {
const isMe = argArray && argArray.length >= 3 && argArray[2] && definePropertyIsMe in argArray[2];
const isDocumentCookie = argArray && argArray.length >= 2 && argArray[0] === document && "cookie" === argArray[1];
if (!isMe && isDocumentCookie) {
if (argArray && argArray.length >= 3) {
realDocumentCookieProperty = argArray[2];
return;
}
}
return target.apply(thisArg, argArray);
}
});
Object.defineProperty.toString = function () {
return "function defineProperty() { [native code] }";
}
Object.defineProperties = new Proxy(Object.defineProperties, {
apply: function (target, thisArg, argArray) {
const isDocumentCookie = argArray && argArray.length >= 2 && document === argArray[0] && "cookie" in argArray[1];
if (isDocumentCookie) {
realDocumentCookieProperty = argArray[1]["cookie"];
delete argArray[1]["cookie"];
if (!Object.keys(argArray[1]).length) {
return;
}
}
return target.apply(thisArg, argArray);
}
});
Object.defineProperties.toString = function () {
return "function defineProperties() { [native code] }";
}
})();
(function addCookieHook() {
const handler = {
get: () => {
delete document.cookie;
try {
if (realDocumentCookieProperty && "get" in realDocumentCookieProperty) {
return realDocumentCookieProperty["get"].apply(document, arguments);
} else {
return document.cookie;
}
} finally {
addCookieHook();
}
}, set: newValue => {
allenyang_onSetCookie(newValue);
delete document.cookie;
try {
if (realDocumentCookieProperty && "set" in realDocumentCookieProperty) {
realDocumentCookieProperty["set"].apply(document, [newValue]);
} else {
document.cookie = newValue;
}
} finally {
addCookieHook();
}
}, configurable: true, enumerable: false,
};
handler[definePropertyIsMe] = true;
Object.defineProperty(document, "cookie", handler);
})();
function allenyang_onSetCookie(newValue) {
const cookiePair = parseSetCookie(newValue);
const currentCookieMap = getCurrentCookieMap();
if (cookiePair.expires !== null && new Date().getTime() >= cookiePair.expires) {
onDeleteCookie(newValue, cookiePair.name, cookiePair.value || (currentCookieMap.get(cookiePair.name) || {}).value);
return;
}
if (currentCookieMap.has(cookiePair.name)) {
onUpdateCookie(newValue, cookiePair.name, currentCookieMap.get(cookiePair.name).value, cookiePair.value);
return;
}
onAddCookie(newValue, cookiePair.name, cookiePair.value);
}
function onReadCookie(cookieOriginalValue, cookieName, cookieValue) {
}
function onDeleteCookie(cookieOriginalValue, cookieName, cookieValue) {
const valueStyle = `color: black; background: #E50000; font-size: ${consoleLogFontSize}px; font-weight: bold;`;
const normalStyle = `color: black; background: #FF6766; font-size: ${consoleLogFontSize}px;`;
const message = [
normalStyle, now(),
normalStyle, "JS Cookie Monitor: ",
normalStyle, "delete cookie, cookieName = ",
valueStyle, `${cookieName}`,
...(() => {
if (!cookieValue) {
return [];
}
return [normalStyle, ", value = ",
valueStyle, `${cookieValue}`,];
})(),
normalStyle, `, code location = ${getCodeLocation()}`];
console.log(genFormatArray(message), ...message);
testDebuggerRules(cookieOriginalValue, "delete", cookieName, cookieValue);
}
function onUpdateCookie(cookieOriginalValue, cookieName, oldCookieValue, newCookieValue) {
const cookieValueChanged = oldCookieValue !== newCookieValue;
if (ignoreUpdateButNotChanged && !cookieValueChanged) {
return;
}
const valueStyle = `color: black; background: #FE9900; font-size: ${consoleLogFontSize}px; font-weight: bold;`;
const normalStyle = `color: black; background: #FFCC00; font-size: ${consoleLogFontSize}px;`;
const message = [
normalStyle, now(),
normalStyle, "JS Cookie Monitor: ",
normalStyle, "update cookie, cookieName = ",
valueStyle, `${cookieName}`,
...(() => {
if (cookieValueChanged) {
return [normalStyle, `, oldValue = `,
valueStyle, `${oldCookieValue}`,
normalStyle, `, newValue = `,
valueStyle, `${newCookieValue}`]
} else {
return [normalStyle, `, value = `,
valueStyle, `${newCookieValue}`,];
}
})(),
normalStyle, `, valueChanged = `,
valueStyle, `${cookieValueChanged}`,
normalStyle, `, code location = ${getCodeLocation()}`];
console.log(genFormatArray(message), ...message);
testDebuggerRules(cookieOriginalValue, "update", cookieName, newCookieValue, cookieValueChanged);
}
function onAddCookie(cookieOriginalValue, cookieName, cookieValue) {
const valueStyle = `color: black; background: #669934; font-size: ${consoleLogFontSize}px; font-weight: bold;`;
const normalStyle = `color: black; background: #65CC66; font-size: ${consoleLogFontSize}px;`;
const message = [
normalStyle, now(),
normalStyle, "JS Cookie Monitor: ",
normalStyle, "add cookie, cookieName = ",
valueStyle, `${cookieName}`,
normalStyle, ", cookieValue = ",
valueStyle, `${cookieValue}`,
normalStyle, `, code location = ${getCodeLocation()}`];
console.log(genFormatArray(message), ...message);
testDebuggerRules(cookieOriginalValue, "add", cookieName, cookieValue);
}
function now() {
return "[" + new Date(new Date().getTime() + 1000 * 60 * 60 * 8).toJSON().replace("T", " ").replace("Z", "") + "] ";
}
function genFormatArray(messageAndStyleArray) {
const formatArray = [];
for (let i = 0, end = messageAndStyleArray.length / 2; i < end; i++) {
formatArray.push("%c%s");
}
return formatArray.join("");
}
function getCodeLocation() {
const callstack = new Error().stack.split("\n");
while (callstack.length && callstack[0].indexOf("allenyang") === -1) {
callstack.shift();
}
callstack.shift();
callstack.shift();
return callstack[0].trim();
}
function parseSetCookie(cookieString) {
// uuid_tt_dd=10_37476713480-1609821005397-659114; Expires=Thu, 01 Jan 1025 00:00:00 GMT; Path=/; Domain=.csdn.net;
const cookieStringSplit = cookieString.split(";");
const {key, value} = splitKeyValue(cookieStringSplit.length && cookieStringSplit[0])
const map = new Map();
for (let i = 1; i < cookieStringSplit.length; i++) {
let {key, value} = splitKeyValue(cookieStringSplit[i]);
map.set(key.toLowerCase(), value);
}
// 当不设置expires的时候关闭浏览器就过期
const expires = map.get("expires");
return new CookiePair(key, value, expires ? new Date(expires).getTime() : null)
}
function splitKeyValue(s) {
let key = "", value = "";
const keyValueArray = (s || "").split("=");
if (keyValueArray.length) {
key = decodeURIComponent(keyValueArray[0].trim());
}
if (keyValueArray.length > 1) {
value = decodeURIComponent(keyValueArray.slice(1).join("=").trim());
}
return {
key, value
}
}
function getCurrentCookieMap() {
const cookieMap = new Map();
if (!document.cookie) {
return cookieMap;
}
document.cookie.split(";").forEach(x => {
const {key, value} = splitKeyValue(x);
cookieMap.set(key, new CookiePair(key, value));
});
return cookieMap;
}
class DebuggerRule {
constructor(eventName, cookieNameFilter, cookieValueFilter) {
this.eventName = eventName;
this.cookieNameFilter = cookieNameFilter;
this.cookieValueFilter = cookieValueFilter;
}
test(eventName, cookieName, cookieValue) {
return this.testByEventName(eventName) && (this.testByCookieNameFilter(cookieName) || this.testByCookieValueFilter(cookieValue));
}
testByEventName(eventName) {
if (!enableEventDebugger[eventName]) {
return false;
}
// 事件不设置则匹配任何事件
if (!this.eventName) {
return true;
}
return this.eventName === eventName;
}
testByCookieNameFilter(cookieName) {
if (!cookieName || !this.cookieNameFilter) {
return false;
}
if (typeof this.cookieNameFilter === "string") {
return this.cookieNameFilter === cookieName;
}
if (this.cookieNameFilter instanceof RegExp) {
return this.cookieNameFilter.test(cookieName);
}
return false;
}
testByCookieValueFilter(cookieValue) {
if (!cookieValue || !this.cookieValueFilter) {
return false;
}
if (typeof this.cookieValueFilter === "string") {
return this.cookieValueFilter === cookieValue;
}
if (this.cookieValueFilter instanceof RegExp) {
return this.cookieValueFilter.test(cookieValue);
}
return false;
}
}
(function standardizingRules() {
const ruleConfigErrorMessage = [];
const newRules = [];
while (debuggerRules.length) {
const rule = debuggerRules.pop();
if (typeof rule === "string" || rule instanceof RegExp) {
newRules.push(new DebuggerRule(null, rule, null));
continue;
}
for (let key in rule) {
let events = null;
let cookieNameFilter = null;
let cookieValueFilter = null;
if (key === "events") {
events = rule["events"] || "add | delete | update";
cookieNameFilter = rule["name"]
cookieValueFilter = rule["value"];
} else if (key !== "name" && key !== "value") {
events = key;
cookieNameFilter = rule[key];
cookieValueFilter = rule["value"];
} else {
continue;
}
if (!cookieNameFilter) {
const errorMessage = `必须为此条规则 ${JSON.stringify(rule)} 配置一个Cookie Name匹配条件`;
ruleConfigErrorMessage.push(errorMessage);
continue;
}
events.split("|").forEach(eventName => {
eventName = eventName.trim();
if (eventName !== "add" && eventName !== "delete" && eventName !== "update") {
const errorMessage = `此条规则 ${JSON.stringify(rule)} 的Cookie事件名字配置错误,必须为 add、delete、update 三种之一或者|分隔的组合,您配置的是 ${eventName},仅忽略此无效事件`;
ruleConfigErrorMessage.push(errorMessage);
return;
}
newRules.push(new DebuggerRule(eventName, cookieNameFilter, cookieValueFilter));
})
}
}
if (ruleConfigErrorMessage.length) {
const errorMessageStyle = `color: black; background: #FF2121; font-size: ${Math.round(consoleLogFontSize * 1.5)}px; font-weight: bold;`;
let errorMessage = now() + "JS Cookie Monitor: 以下Cookie断点规则配置错误,已忽略: \n ";
for (let i = 0; i < ruleConfigErrorMessage.length; i++) {
errorMessage += `${i + 1}. ${ruleConfigErrorMessage[i]}\n`;
}
console.log("%c%s", errorMessageStyle, errorMessage);
}
for (let rule of newRules) {
debuggerRules.push(rule);
}
})();
function testDebuggerRules(setCookieOriginalValue, eventName, cookieName, cookieValue, cookieValueChanged) {
for (let rule of debuggerRules) {
if (rule.test(eventName, cookieName, cookieValue)) {
debugger;
}
}
}
class CookiePair {
constructor(name, value, expires) {
this.name = name;
this.value = value;
this.expires = expires;
}
}
}
)();
建议配合油猴食用效果更佳~