您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Send the current website to Hunter
// ==UserScript== // @name Hunter view // @namespace http://tampermonkey.net/ // @version 0.5 // @description Send the current website to Hunter // @author 0cat // @match http://*/* // @match https://*/* // @grant GM_registerMenuCommand // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @connect * // @license MIT // ==/UserScript== GM_registerMenuCommand("Hunter Login", openLoginForm, "l"); GM_registerMenuCommand("Hunter 识别结果", HunterFind, "s"); // 创建自定义输入表单 function openLoginForm() { const body = document.getElementsByTagName('body')[0]; const loginDiv = document.createElement('div'); loginDiv.innerHTML = ` <div style="position:fixed; top:50%; left:50%; transform:translate(-50%, -50%); background-color:white; padding:20px; border-radius:10px; box-shadow:0 0 10px rgba(0, 0, 0, 0.5); z-index:100000;"> <h3>Login to Hunter</h3> <label>Username (一般是手机号): <input type="text" id="hunterUsername" style="width:100%;" placeholder="e.g., 1234567890" /> </label><br/><br/> <label>Hunter ApiKey: <input type="password" id="hunterKey" style="width:100%;" /> </label><br/><br/> <button id="loginSubmit">Submit</button> <button id="loginCancel">Cancel</button> </div> `; body.appendChild(loginDiv); document.getElementById('loginSubmit').onclick = function() { const username = document.getElementById('hunterUsername').value; const HunterKey = document.getElementById('hunterKey').value; if (username && HunterKey) { GM_setValue("username", username); GM_setValue("HunterKey", HunterKey); alert("Login successful!"); body.removeChild(loginDiv); // Remove form after login } else { alert("Please fill in both fields."); } }; document.getElementById('loginCancel').onclick = function() { body.removeChild(loginDiv); }; } function HunterFind() { var username = GM_getValue("username"); var HunterKey = GM_getValue("HunterKey"); if (!username || !HunterKey) { console.error("No username or HunterKey found"); return; } const body = document.getElementsByTagName('body')[0]; const div = document.createElement('div'); div.innerHTML = `<div style="font-size:14px;color:rgba(0,0,0,0.65);box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1) ;background-color: #fff;border-radius: 5px;border: 1px solid #ebebeb;right:20px;top:20px;position: fixed;z-index: 1000000"> <div style="display: flex;justify-content: flex-end;margin-right: 8px;"> <div id="expandBtn" style="color:rgb(24, 36, 127);cursor: pointer;margin-left: 8px;margin-top: 4px;font-size: 12px;margin-bottom: 4px;display: none">Hunter展开</div> <div id="hideBtn" style="cursor: pointer;font-size: 12px;margin-top: 2px;color:rgb(96, 98, 102);">隐藏</div> </div> <div id="contentDiv" class="plugs_content" style="padding-bottom: 10px;"> <div style="margin-left: 4px;border-radius: 2px;display: inline-block;padding: 8px;padding-right: 40px;"> <div style="margin-bottom: 4px;display: flex"> <div style="margin-right: 6px;white-space: nowrap">RDAP_IP_CIDR:</div> <div class="rdap_cidr" style="word-wrap:break-word;word-break:normal;overflow: hidden;">N/A</div> </div> <div style="margin-bottom: 4px;display: flex"> <div style="margin-right: 6px;white-space: nowrap">RDAP_描述:</div> <div class="rdap_description" style="word-wrap:break-word;word-break:normal;overflow: hidden;">N/A</div> </div> <div style="margin-bottom: 4px;display: flex"> <div style="margin-right: 6px;white-space: nowrap">地区:</div> <div class="area" style="word-wrap:break-word;word-break:normal;overflow: hidden;">null</div> </div> <div style="margin-bottom: 4px;display: flex"> <div style="margin-right: 6px;white-space: nowrap">运营商和org:</div> <div class="org" style="word-wrap:break-word;word-break:normal;overflow: hidden;">null</div> </div> <div style="margin-bottom: 4px;display: flex"> <div style="margin-right: 6px;white-space: nowrap">ICP:</div> <div class="icpnumber" style="word-wrap:break-word;word-break:normal;overflow: hidden;">null</div> </div> <div style="margin-bottom: 4px;display: flex"> <div style="margin-right: 6px;white-space: nowrap">协议:</div> <div class="protocol" style="word-wrap:break-word;word-break:normal;overflow: hidden;">null</div> </div> <div style="margin-bottom: 4px;display: flex"> <div style="margin-right: 6px;white-space: nowrap">端口:</div> <div class="port">null</div> </div> <div style="margin-bottom: 4px;display: flex"> <div style="margin-right: 6px;white-space: nowrap">剩余积分:</div> <div class="surplus">null</div> </div> </div> <div class="copy-text-data" style="cursor: pointer;display: flex;margin-top: 8px;border-top:1px solid #ebebeb;justify-content: flex-end;padding-right: 4px;"> 复制 </div> <ul class="demo1" style="width: 100%;max-height: 350px;max-width: 600px; overflow: auto;"> <div style="display: flex;margin-top: 8px;"> <div style="padding: 0 10px;"> <div class="table_title">null</div> </div> <div style="padding: 0 10px;"> <div class="table_protocol">null</div> </div> <div style="padding: 0 10px;"> <div class="table_port">null</div> </div> <div style="padding: 0 10px;"> <div class="table_code">null</div> </div> <div style="padding: 0 10px;"> <div class="table_url"> <div> <a href="" class="url1" style="color: #1890ff;text-decoration: none">url</a> </div> </div> </div> </div> </ul> </div> </div>`; body.appendChild(div); // 隐藏/展开功能 const hideBtn = document.getElementById('hideBtn'); const expandBtn = document.getElementById('expandBtn'); const contentDiv = document.getElementById('contentDiv'); hideBtn.onclick = function() { contentDiv.style.display = 'none'; hideBtn.style.display = 'none'; expandBtn.style.display = 'block'; }; expandBtn.onclick = function() { contentDiv.style.display = 'block'; hideBtn.style.display = 'block'; expandBtn.style.display = 'none'; }; var target = window.location.hostname; var isValidIP_reg = /(\d{1,3}\.){3}\d{1,3}/; // 如果是 IP,则执行 RDAP 查询 if (isValidIP_reg.test(target)) { // 发起 RDAP 请求 var rdap_url = `https://rdap.apnic.net/ip/${target}`; GM_xmlhttpRequest({ method: "GET", url: rdap_url, onload: function(xhr) { if (xhr.status !== 200) { console.error("RDAP Request failed, status code:", xhr.status); return; } const rdap_res = JSON.parse(xhr.responseText); // 提取 CIDR 和描述 const handle = rdap_res.handle; const cidr = rdap_res.cidr0_cidrs[0].v4prefix + "/" + rdap_res.cidr0_cidrs[0].length; const description = rdap_res.remarks[0].description[0] || 'N/A'; // 将 CIDR 和描述信息填充到小卡片中 const rdap_cidr_element = document.getElementsByClassName('rdap_cidr')[0]; const rdap_description_element = document.getElementsByClassName('rdap_description')[0]; rdap_cidr_element.textContent = cidr || 'N/A'; rdap_description_element.textContent = description || 'N/A'; }, onerror: function(xhr) { console.error("RDAP Request error:", xhr); } }); } var Hunter_url = "https://hunter.qianxin.com/openApi/search?username=" + username + "&api-key=" + HunterKey + "&page=1&page_size=50&is_web=3&start_time=" + getNewDate("before", 6) + "&end_time=" + getNewDate() + "&search="; var search, url; if (isValidIP_reg.test(target)) { search = btoa('ip=="' + target + '"'); url = Hunter_url + search; } else { search = btoa('domain=="' + target + '"'); url = Hunter_url + search; } if (!url) { console.error("URL generation failed."); return; } // 发起 Hunter 查询请求 try { GM_xmlhttpRequest({ method: "GET", url: url, onload: function(xhr) { if (xhr.status !== 200) { console.error("Hunter Request failed, status code:", xhr.status); return; } const res = JSON.parse(xhr.responseText); const target_data = res.data?.arr || []; if (target_data.length === 0) { return; } // 获取 HTML 元素 const area = document.getElementsByClassName('area')[0]; const org = document.getElementsByClassName('org')[0]; const protocol = document.getElementsByClassName('protocol')[0]; const port = document.getElementsByClassName('port')[0]; const surplus = document.getElementsByClassName('surplus')[0]; const icpnumber = document.getElementsByClassName('icpnumber')[0]; const table_title = document.getElementsByClassName('table_title')[0]; const table_protocol = document.getElementsByClassName('table_protocol')[0]; const table_port = document.getElementsByClassName('table_port')[0]; const table_code = document.getElementsByClassName('table_code')[0]; const table_url = document.getElementsByClassName('table_url')[0]; // 遍历数据并展示到页面 let title_innerHTML = `<div style='white-space: nowrap'>标题</div>`; let protocol_innerHTML = `<div style='white-space: nowrap'>协议</div>`; let port_innerHTML = `<div style='white-space: nowrap'>端口</div>`; let code_innerHTML = `<div style='white-space: nowrap'>状态码</div>`; let url_innerHTML = `<div style='white-space: nowrap'>url</div>`; target_data.forEach(item => { title_innerHTML += `<div style="white-space: nowrap;height: 20px">${item.web_title || 'N/A'}</div>`; protocol_innerHTML += `<div style="white-space: nowrap;height: 20px">${item.protocol || 'N/A'}</div>`; code_innerHTML += `<div style="white-space: nowrap;height: 20px">${item.status_code || 'N/A'}</div>`; url_innerHTML += `<div style="white-space: nowrap;height: 20px"><a href="${item.url}" target='_blank' style="color: #1890ff;text-decoration: none">${item.url}</a></div>`; port_innerHTML += `<div style="white-space: nowrap;height: 20px">${item.port || 'N/A'}</div>`; }); table_title.innerHTML = title_innerHTML; table_protocol.innerHTML = protocol_innerHTML; table_code.innerHTML = code_innerHTML; table_port.innerHTML = port_innerHTML; table_url.innerHTML = url_innerHTML; const target_location = target_data[0] || {}; area.textContent = [target_location.country || '', target_location.province || '', target_location.city || ''].filter(item => item).join('-') || 'N/A'; org.textContent = Array.from(new Set(target_data.filter(item => item.as_org).map(item => item.as_org))).join(',') || 'N/A'; icpnumber.textContent = target_data[0].number || 'N/A'; protocol.textContent = Array.from(new Set(target_data.filter(item => item.protocol).map(item => item.protocol))).join(',') || 'N/A'; port.textContent = Array.from(new Set(target_data.filter(item => item.port).map(item => item.port))).join(',') || 'N/A'; surplus.textContent = res.data.rest_quota?.split(':')[1] || 'N/A'; }, onerror: function(xhr) { console.error("Hunter Request error:", xhr); } }); } catch (e) { console.error("Error while sending request:", e); } } function getNewDate(flag, many) { const thirtyDays = [4, 6, 9, 11]; const thirtyOneDays = [1, 3, 5, 7, 8, 10, 12]; const currDate = new Date(); const year = currDate.getFullYear(); let month = currDate.getMonth() + 1; let countDays = 0; let targetDateMilli = 0; let GMTDate = ''; let targetYear = ''; let targetMonth = ''; let targetDate = ''; let dealDate = ''; let hh = currDate.getHours() < 10 ? "0" + currDate.getHours() : currDate.getHours(); let mm = currDate.getMinutes() < 10 ? "0" + currDate.getMinutes() : currDate.getMinutes(); let ss = currDate.getSeconds() < 10 ? "0" + currDate.getSeconds() : currDate.getSeconds(); const isLeapYear = !!((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); for (let i = 0; i < many; i++) { if (flag === 'before') { month = month - 1 <= 0 ? 12 : month - 1; } else { month = month + 1 > 12 ? 1 : month + 1; } thirtyDays.includes(month) ? (countDays += 30) : thirtyOneDays.includes(month) ? (countDays += 31) : isLeapYear ? (countDays += 29) : (countDays += 28); } targetDateMilli = currDate.setDate(currDate.getDate() - (flag === 'before' ? countDays : countDays * -1)); GMTDate = new Date(targetDateMilli); targetYear = GMTDate.getFullYear(); targetMonth = GMTDate.getMonth() + 1; targetDate = GMTDate.getDate(); targetMonth = targetMonth.toString().padStart(2, '0'); targetDate = targetDate.toString().padStart(2, '0'); dealDate = `${targetYear}-${targetMonth}-${targetDate} ${hh}:${mm}:${ss}`; return escape(dealDate); }