您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Utilize password database to crack torn cracking crime.
// ==UserScript== // @name torn-crime-crack-helper // @namespace nodelore.torn.crack-helper // @version 1.2.4 // @description Utilize password database to crack torn cracking crime. // @author nodelore[2786679] NEvaldas[352097] // @match https://www.torn.com/loader.php?sid=crimes* // @grant GM_getValue // @grant GM.getValue // @grant GM_setValue // @grant GM.setValue // @grant GM_xmlhttpRequest // @grant GM_addStyle // @license MIT // ==/UserScript== (function () { "use strict"; // Avoid duplicate injection if (window.CRACK_HELPER_INJECTED) { return; } window.CRACK_HELPER_INJECTED = true; const cracker_record = {}; const filter_history = {}; const isMobile = () => { return window.innerWidth <= 768; }; let inPDA = false; const PDAKey = "###PDA-APIKEY###"; if (PDAKey.charAt(0) !== "#") { inPDA = true; } const http_get = (url, success, failed) => { GM_xmlhttpRequest({ method: "get", url: url, timeout: 30000, ontimeout: (err) => { failed(err); }, onerror: (err) => { failed(err); }, onload: (res) => { success(res); }, }); }; // ========================= Configuration============================================================================================================================== const CRACKER_STATUS_KEY = "CRACKER_STATUS"; const defaultSel = isMobile() ? "100k" : "1m"; let CRACKER_SEL = localStorage.getItem(CRACKER_STATUS_KEY) || defaultSel; const LIMIT = 10; // add custom password list here, and set CRACKER_SEL to the one you want to choose const PASSWORD_DATABASE = { "10m": "https://raw.githubusercontent.com/ignis-sec/Pwdb-Public/master/wordlists/ignis-10M.txt", "1m": "https://raw.githubusercontent.com/ignis-sec/Pwdb-Public/master/wordlists/ignis-1M.txt", "1m_alter": "https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10-million-password-list-top-1000000.txt", "100k": "https://raw.githubusercontent.com/ignis-sec/Pwdb-Public/master/wordlists/ignis-100K.txt", "100k_alter": "https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10-million-password-list-top-100000.txt", "10k": "https://raw.githubusercontent.com/ignis-sec/Pwdb-Public/master/wordlists/ignis-10K.txt", "1k": "https://raw.githubusercontent.com/ignis-sec/Pwdb-Public/master/wordlists/ignis-1K.txt", }; // ===================================================================================================================================================================== window.CRACK_HELPER_INJECTED = true; if (GM) { window.GM_getValue = GM.getValue; window.GM_setValue = GM.setValue; } if (!PASSWORD_DATABASE[CRACKER_SEL]) { console.log("Fail to fetch cracker password"); return; } const CRACKER_HELPER_KEY = "CRACKER_HELPER_STORAGE"; let cracker_helper = { source: "", data: [], }; let titleInterval, updateInterval; let is_injected = false; const setCrackTitle = (title) => { if (titleInterval) { clearInterval(titleInterval); } titleInterval = setInterval(() => { const titleQuery = "div[class*=titleBar___] div[class*=title___]"; if ($(titleQuery).length > 0) { $(titleQuery).text(`CRACKING (${title})`); clearInterval(titleInterval); titleInterval = undefined; } }, 1000); }; const fetch_action = (useCache = true) => { setCrackTitle("Loading from network"); http_get( PASSWORD_DATABASE[CRACKER_SEL], (res) => { const text = res.responseText; cracker_helper.data = []; text.split("\n").forEach((pwd) => { cracker_helper.data.push(pwd.trim().replace("\n", "")); }); cracker_helper.source = PASSWORD_DATABASE[CRACKER_SEL]; if (useCache) { GM_setValue(CRACKER_HELPER_KEY, cracker_helper); } setCrackTitle("Loaded"); updatePage(); console.log("load cracker_helper from network:"); console.log(cracker_helper); }, (res) => { console.error(`error: ${res}`); } ); }; const insertSelector = () => { let options = ""; for (let abbr in PASSWORD_DATABASE) { options += `<option value="${abbr}">${abbr}</option>`; } const selector = $(` <div class="cracker-helper-selector"> <label>Source:</label> <select name="crackerSel"> ${options} </select> </div> `); selector.find("select").val(CRACKER_SEL); selector.find("select").change(function () { CRACKER_SEL = $(this).val(); localStorage.setItem(CRACKER_STATUS_KEY, CRACKER_SEL); $("div.cracker-helper-panel").each(function () { $(this).remove(); }); fetch_action(); }); if ($("div.cracker-helper-selector").length == 0) { $("h4[class*=heading___]").after(selector); } }; const addStyle = () => { const styles = ` .cracker-helper-selector{ display: flex; align-items: center; font-size: 14px; font-weight: bold; } .cracker-helper-selector select{ background: transparent; text-align: center; border: none; } .dark-mode .cracker-helper-selector select{ color: #F2F2F2 !important; } .dark-mode .cracker-helper-selector select option{ background: #333 !important; color: #F2F2F2 !important; } .cracker-helper-panel{ width: 100%; height: 30px; background: #F2F2F2; box-sizing: border-box; display: flex; padding: 5px; border-bottom: 1px solid rgba(1, 1, 1, .1); } .cracker-helper-panel:hover{ background: #FFF; } .dark-mode .cracker-helper-panel{ background: rgba(1, 1, 1, .15) !important; border-bottom: 1px solid #222 !important; } .dark-mode .cracker-helper-panel:hover{ background: rgba(1, 1, 1, .15) !important; } .cracker-current-status{ display: flex; flex-flow: column nowrap; border-right: 1px solid; border-image-source: linear-gradient(180deg,transparent,#ddd 53%,transparent); border-image-slice: 1; box-sizing: border-box; justify-content: center; padding-left: 5px; } .dark-mode .cracker-current-status{ border-image-source: linear-gradient(180deg,transparent,#000,transparent); } .cracker-helper-panel-mobile .cracker-current-status{ fotn-size: 5px !important; } .cracker-current-status div{ width: 100%; color: #000; font: inherit; color: #666; } .cracker-status-count{ color: #c66231 !important; } .cracker-current-result{ flex: 1; display: flex; align-items: center; border-right: 1px solid; border-image-source: linear-gradient(180deg,transparent,#ddd 53%,transparent); border-image-slice: 1; box-sizing: border-box; padding-left: 5px; font-size: 1.25em; } .dark-mode .cracker-current-result{ border-image-source: linear-gradient(180deg,transparent,#000,transparent); } .cracker-result-item{ border: 1px solid rgba(1, 1, 1, .1); height: 34px; line-height: 34px; font-size: 100%; text-align: center; margin-left: 6px; box-sizing: border-box; } .dark-mode .cracker-result-item{ border-color: #F2F2F266 !important; } .cracker-helper-panel-mobile .cracker-result-item{ margin-left: 3px !important; } .cracker-button-set{ display: flex; flex-flow: row nowrap; justify-content: space-between; align-items: center; box-sizing: border-box; padding-right: 5px; } .cracker-helper-panel-mobile .cracker-button-set{ flex-flow: column nowrap !important; } .cracker-button-set button{ text-align: center; height: 24px; line-height: 24px; } .cracker-helper-panel-mobile .cracker-button-set button{ width: 60px; height: 18px !important; line-height: 18px !important; } `; const isTampermonkeyEnabled = typeof unsafeWindow !== "undefined"; if (isTampermonkeyEnabled) { GM_addStyle(styles); } else { let style = document.createElement("style"); style.type = "text/css"; style.innerHTML = styles; document.head.appendChild(style); } }; // under experiment const findCommonCandidates = (arr, target) => { if (arr.length === 0) { return arr; } const target_poses = []; for (let i = 0; i < target.length; i++) { const c = target[i]; if (c == ".") { target_poses.push(i); } } const freqs = []; for (let pos of target_poses) { const freq = {}; for (let res of arr) { const c = res[pos]; if (!freq[c]) { freq[c] = 0; } freq[c] += 1; } const freq_list = Object.entries(freq); freq_list.sort((a, b) => { if (a[1] > b[1]) { return -1; } else if (a[1] < b[1]) { return 1; } return 0; }); freqs.push({ pos: pos, char: freq_list[0][0], count: freq_list[0][1], }); } freqs.sort((a, b) => { if (a["count"] > b["count"]) { return -1; } else if (a["count"] < b["count"]) { return 1; } return 0; }); const res = []; const highest = freqs[0]; const highest_pos = highest["char"]; const highest_char = highest["pos"]; for (let c of arr) { if (c[highest_pos] === highest_char) { res.unshift(c); } else { res.push(c); } } return { res, highest_pos, highest_char, }; }; let global_index = 0; const handleCrime = ( item, extraInfo = undefined, panel_index = undefined ) => { let index = panel_index; if (index) { index = parseInt(index); } let target = ""; if (extraInfo) { target = extraInfo; } else { item.find("div[class*=charSlot_]").each(function () { const val = $(this).text().trim(); if (val == "") { target += "."; } else { target += val; } }); } target = target.replaceAll("ø", "0"); let targetRegex = new RegExp(`^${target}$`); // console.log(`target Regex is ${targetRegex}, index is ${index}, globalIndex: ${global_index}`); setCrackTitle("Calculating"); let result = cracker_helper.data.filter((item) => targetRegex.test(item)); if (result.length === 0 && target.length > 6) { let found = false; let splitIndex = 3; // when regex match does not work, we will split the regex and try to find out result for both side while (!found && splitIndex < 7 && splitIndex < target.length - 1) { const regexLeft = new RegExp(`^${target.substring(0, splitIndex)}$`); const regexRight = new RegExp(`^${target.substring(splitIndex)}$`); splitIndex += 1; const leftResult = cracker_helper.data.filter((item) => regexLeft.test(item) ); const rightResult = cracker_helper.data.filter((item) => regexRight.test(item) ); if (leftResult.length > 0 && rightResult.length > 0) { const minSize = Math.min(leftResult.length, rightResult.length); result = leftResult .map((item, index) => { if (index < minSize) { return item + rightResult[index]; } }) .filter((item) => item !== undefined); if (result.length > 10) { found = true; } } } } if (filter_history[index]) { result = result.filter((item) => { for (let history of filter_history[index]) { const char = history.char; const charPos = history.charPos; if (item && item[charPos] === char) { return false; } } return true; }); } result = result.slice(0, LIMIT); if (result.length > 0) { if (index) { cracker_record[index] = result; } else { cracker_record[global_index] = result; } } setCrackTitle("Done"); let found = item.find(".cracker-helper-panel"); if (found.length == 0) { const detailPanel = $(` <div class="cracker-helper-panel" data-attr=${global_index}> <div class="cracker-current-status"> <div class="cracker-status-count">Top ${result.length} candidates:</div> </div> <div class="cracker-current-result"> </div> <div class="cracker-button-set" data-index="0"> <button title="previous one" data-attr=${global_index} class="torn-btn cracker-button-prev" data-action="prev" >Prev</button> <button title="next one" data-attr=${global_index} class="torn-btn cracker-button-next" data-action="next">Next</button> </div> </div> `); if (index) { detailPanel.attr("data-attr", index); detailPanel.find("button.cracker-button-prev").attr("data-attr", index); detailPanel.find("button.cracker-button-next").attr("data-attr", index); } else { global_index += 1; } if (window.innerWidth < 1800) { detailPanel.addClass("cracker-helper-panel-minimize"); } if (result[0]) { for (let char of result[0]) { detailPanel.find(".cracker-current-result").append( $(` <div class="cracker-result-item" style="width: ${ item.find("div[class*=charSlot]").width() + 2 }px"> ${char.toUpperCase()} </div> `) ); } } detailPanel.find(".cracker-button-set").css({ width: item.find("div[class*=guessesLeftSection]").width() + item.find("div[class*=commitButtonSection]").width() + 5, "padding-left": item.find("div[class*=guessesLeftSection]").width(), }); if (isMobile()) { detailPanel.addClass("cracker-helper-panel-mobile"); detailPanel.find(".cracker-current-status").css({ width: "33px", "font-size": "7px", border: "none", }); } else { detailPanel.find(".cracker-current-status").css({ width: item.find("div[class*=targetSection]").width() + 5, }); } detailPanel.css({ left: item.offset().left + item.width() + 10, top: item.offset().top, height: item.height(), }); detailPanel.find(".cracker-button-set button").click(function () { const action = $(this).attr("data-action"); const action_index = $(this).attr("data-attr"); let current_index = parseInt($(this).parent().attr("data-index")); const action_record = cracker_record[action_index]; if (action_record) { const record_length = action_record.length; if (action === "next") { if (current_index < record_length - 1) { current_index += 1; } } else if (action === "prev") { if (current_index > 0) { current_index -= 1; } } $(this).parent().attr("data-index", current_index); $(this) .parent() .parent() .find(".cracker-status-text") .text(action_record[current_index]); let index = 0; if (action_record[current_index]) { for (let char of action_record[current_index]) { $(this) .parent() .parent() .find(`div.cracker-result-item:eq(${index++})`) .text(char.toUpperCase()); } } } else { console.error("Fail to fetch record detail"); console.log( `action_index: ${action_index}, action_record: ${action_record}` ); console.log(cracker_record); } }); item.find("div[class*=sections]").after(detailPanel); } else { const currentIdx = parseInt(found.attr("data-attr")); if (index && currentIdx < 10000) { found.attr("data-attr", index); found.find("button.cracker-button-prev").attr("data-attr", index); found.find("button.cracker-button-next").attr("data-attr", index); cracker_record[index] = cracker_record[currentIdx]; delete cracker_record[currentIdx]; } found.find(".cracker-info-text").text(targetRegex); found .find(".cracker-status-count") .text(`Top ${result.length} candidates:`); let idx = 0; if (result[0]) { for (let char of result[0]) { found .find(`div.cracker-result-item:eq(${idx++})`) .text(char.toUpperCase()); } } else { found.find(`div.cracker-result-item`).remove(); } found.find(".cracker-button-set").attr("data-index", "0"); } // Fix issue due to dynamic loading // TODO: existing approach is not complete, it will fail sometime at bruteforce const parent = item.parent().parent()[0]; let parentNewHeight = 102; if ($(parent).hasClass("outcome-expanded")) { parentNewHeight += 150; } $(parent).css("height", `${parentNewHeight}px`); const nextSibling = parent.nextSibling; const currentTranslate = getTranslate(nextSibling); let heightUpToThisItem = 0; let previousSibling = parent; while (previousSibling) { if ($(previousSibling).height() > 0) heightUpToThisItem += parseInt( $(previousSibling).css("height").replace("px", "") ); previousSibling = previousSibling.previousElementSibling || previousSibling.previousSibling; } const newNextHeight = heightUpToThisItem; if (newNextHeight > currentTranslate[1] ?? 0) { $(nextSibling).css( "translate", `${currentTranslate[0]}px ${newNextHeight}px` ); } function getTranslate(panel) { return $(panel) .css("translate") .split(" ") .map((x) => parseInt(x.replace("px", ""))); } }; function fixCrimeContainer(mutations) { handlePage($(".crime-option")); } let crimeContainerObserver; const handlePage = (crimes) => { if (!crimeContainerObserver) { crimeContainerObserver = new MutationObserver(fixCrimeContainer); } crimeContainerObserver.observe($("div[class*=virtualList_]")[0], { childList: true, }); for (let i = 0; i < crimes.length; i++) { const target = initial_targets[i]; let crime_id; if (target) { crime_id = target["ID"]; } handleCrime($(crimes[i]), undefined, crime_id); } }; const updatePage = () => { if (location.href.endsWith("cracking")) { inject_once(); insertSelector(); setCrackTitle("Loading"); const crimes = $(".crime-option"); if (crimes.length < 1) { if (!updateInterval) { updateInterval = setInterval(() => { if ( $(".crime-option").length > 0 && cracker_helper.data.length > 0 ) { handlePage($(".crime-option")); clearInterval(updateInterval); updateInterval = undefined; } }, 1000); } } else { handlePage(crimes); } } else { crimeContainerObserver?.disconnect(); crimeContainerObserver = null; $(".cracker-helper-panel").each(function () { $(this).remove(); }); $("div.cracker-helper-selector").remove(); } }; const handleCrackPerpare = (params, data) => { setTimeout(() => { const crimeValue = parseInt(params.get("value1")); if (!params.get("value2")) { handlePage($(".crime-option")); return; } const char = params.get("value2").toLowerCase(); const charPos = parseInt(params.get("value3")); const targets = data["DB"]["crimesByType"]["targets"]; for (let i = 0; i < targets.length; i++) { const target = targets[i]; const target_id = target.ID; const target_panel = $(`.cracker-helper-panel:eq(${i})`); const target_index = parseInt(target_panel.attr("data-attr")); if (target_index < 10000) { target_panel.attr("data-attr", target_id); target_panel .find("button.cracker-button-prev") .attr("data-attr", target_id); target_panel .find("button.cracker-button-next") .attr("data-attr", target_id); cracker_record[target_id] = cracker_record[target_index]; delete cracker_record[target_index]; } if (target_id === crimeValue) { const currentChar = target["password"][charPos]["char"].toString(); if (currentChar !== char) { if (!filter_history[target_id]) { filter_history[target_id] = []; } filter_history[target_id].push({ char, charPos, }); handleCrime(target_panel.parent(), undefined, target_id); // handlePage($(".crime-option")); } } } handlePage($(".crime-option")); }, 25); }; const handleCrackAttempt = (params, data) => { setTimeout(() => { try { const crimeID = parseInt(params.get("crimeID")); if (crimeID === 205) { const crimeValue = parseInt(params.get("value1")); const targets = data["DB"]["crimesByType"]["targets"]; for (let i = 0; i < targets.length; i++) { const target = targets[i]; const target_id = target.ID; const target_panel = $(`.cracker-helper-panel:eq(${i})`); let targetChars; if (target_id === crimeValue) { const target_index = parseInt(target_panel.attr("data-attr")); if (target_index < 10000) { target_panel.attr("data-attr", target_id); target_panel .find("button.cracker-button-prev") .attr("data-attr", target_id); target_panel .find("button.cracker-button-next") .attr("data-attr", target_id); cracker_record[target_id] = cracker_record[target_index]; delete cracker_record[target_index]; } targetChars = target.password .map((x) => (x.char && x.char !== "*" ? x.char : ".")) .join(""); } handleCrime(target_panel.parent(), targetChars, target_id); } } } catch (err) { console.log(err); } }, 25); }; let initial_targets = []; const handleCrackList = (data) => { const targets = data["DB"]["crimesByType"]["targets"]; if (initial_targets.length === 0) { initial_targets = targets; if ($(".cracker-helper-panel").length > 0) { for (let i = 0; i < initial_targets.length; i++) { const target_id = initial_targets[i]["ID"]; const target_panel = $(`.cracker-helper-panel:eq(${i})`); const target_index = parseInt(target_panel.attr("data-attr")); target_panel.attr("data-attr", target_id); target_panel .find("button.cracker-button-prev") .attr("data-attr", target_id); target_panel .find("button.cracker-button-next") .attr("data-attr", target_id); cracker_record[target_id] = cracker_record[target_index]; delete cracker_record[target_index]; } } } }; const interceptFetch = () => { const targetWindow = typeof unsafeWindow !== "undefined" ? unsafeWindow : window; const origFetch = targetWindow.fetch; targetWindow.fetch = async (...args) => { const rsp = await origFetch(...args); const url = new URL(args[0], location.origin); const params = new URLSearchParams(url.search); if ( url.pathname === "/loader.php" && params.get("sid") === "crimesData" ) { const step = params.get("step"); const clonedRsp = rsp.clone(); if (step === "prepare") { handleCrackPerpare(params, await clonedRsp.json()); } else if (step === "attempt") { handleCrackAttempt(params, await clonedRsp.json()); } else if (step === "crimesList") { handleCrackList(await clonedRsp.json()); } } return rsp; }; }; const inject_once = () => { if (is_injected) { return; } addStyle(); interceptFetch(); try { if (inPDA) { console.log(`Load password list for PDA`); fetch_action(false); } else { GM.getValue(CRACKER_HELPER_KEY, cracker_helper) .then((cracker) => { cracker_helper = cracker; if (cracker_helper.source == PASSWORD_DATABASE[CRACKER_SEL]) { setCrackTitle("Loaded"); updatePage(); console.log("load cracker_helper from cache at Desktop:"); console.log(cracker_helper); } else { fetch_action(); } }) .catch(() => { fetch_action(false); }); } } catch (err) { console.log(err); } is_injected = true; }; console.log("Userscript cracker helper starts"); updatePage(); window.onhashchange = () => { updatePage(); }; const bindEventListener = function (type) { const historyEvent = history[type]; return function () { const newEvent = historyEvent.apply(this, arguments); const e = new Event(type); e.arguments = arguments; window.dispatchEvent(e); return newEvent; }; }; history.pushState = bindEventListener("pushState"); window.addEventListener("pushState", function (e) { updatePage(); }); })();