├── doc └── images │ ├── key.png │ ├── anniu.png │ ├── dakai.png │ ├── fofa.PNG │ ├── fofa.png │ ├── huoqu.png │ ├── logo.png │ ├── yunxu.png │ ├── denglu.png │ ├── gengxin.png │ ├── gengxin1.png │ ├── paichu.png │ ├── shibie.png │ ├── shouji.png │ └── zhanshi.png ├── .gitignore ├── LICENSE ├── README.md ├── Fofa_view.js └── Hunter_view.js /doc/images/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/key.png -------------------------------------------------------------------------------- /doc/images/anniu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/anniu.png -------------------------------------------------------------------------------- /doc/images/dakai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/dakai.png -------------------------------------------------------------------------------- /doc/images/fofa.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/fofa.PNG -------------------------------------------------------------------------------- /doc/images/fofa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/fofa.png -------------------------------------------------------------------------------- /doc/images/huoqu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/huoqu.png -------------------------------------------------------------------------------- /doc/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/logo.png -------------------------------------------------------------------------------- /doc/images/yunxu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/yunxu.png -------------------------------------------------------------------------------- /doc/images/denglu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/denglu.png -------------------------------------------------------------------------------- /doc/images/gengxin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/gengxin.png -------------------------------------------------------------------------------- /doc/images/gengxin1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/gengxin1.png -------------------------------------------------------------------------------- /doc/images/paichu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/paichu.png -------------------------------------------------------------------------------- /doc/images/shibie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/shibie.png -------------------------------------------------------------------------------- /doc/images/shouji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/shouji.png -------------------------------------------------------------------------------- /doc/images/zhanshi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0x727/Space_view/HEAD/doc/images/zhanshi.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | .idea 8 | # Test binary, built with `go test -c` 9 | *.test 10 | .DS_Store 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | *.tmp 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 0x727 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![logo](./doc/images/logo.png) 3 | 4 | # Space_view 5 | 6 | 郑重声明:文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担。 7 | 8 | 感谢[lhc0229](https://github.com/lhc0229)、[倾旋](https://github.com/Rvn0xsy)和[alex9968](https://github.com/alex9968)的答疑解惑,以及lhc0229对这个项目前端的支持。 9 | 10 | 11 | 12 | | 类别 | 说明 | 13 | | ---- | --- | 14 | | 作者 | [0cat](https://github.com/0cat-r) | 15 | | 团队 | [0x727](https://github.com/0x727) 未来一段时间将陆续开源工具 | 16 | | 定位 | 直观了解网站资产(通过Hunter或者Fofa),自动化,交互方便,美观| 17 | | 语言 | js.... | 18 | | 功能 | 直观展示网站资产,资产有及时性,浮窗化交互,可自动跳转.... | 19 | 20 | 21 | 22 | 23 | ## 什么是 【Space_view】 ? 24 | 25 | Space_view 是一款通过Hunter(鹰图平台)或者Fofa资产展示的浏览器油猴插件脚本。 26 | 27 | 建议fofa和hunter一起装,左fofa,右hunter! 28 | 29 | ## Why 【Space_view】 ? 30 | 31 | 网络安全从业者需要对某一些授权的网站进行快速的资产了解。 32 | 33 | Fofa和Hunter平台各具优势,通过这个油猴插件可以快速直观的了解网站的资产情况。 34 | 35 | 资产的准确度: 36 | 37 | >采用“==”精准匹配 38 | 39 | 资产的时效性: 40 | 41 | >Hunter是搜索当前站点在平台上距离当前时间前6个月的资产情况(本来是三个月 但是为了数据的隐藏价值 所以改成6个月),以及包括对闰年,某些月份是30天的判断。 42 | 43 | >Fofa是默认搜索当前站点在平台上距离当前时间前一年的资产情况。 44 | 45 | 资产的直观性: 46 | 47 | >title port 等等 包括去重 (Fofa由于平台本身api的问题 isp和org信息暂时无法查出 所以这块数据是空的) 48 | 49 | 50 | 交互的友好型: 51 | 52 | >想看的时候点击右上角就出现小模块,点击就展开显示数据详情,不看的时候就收回,Hunter固定在页面右上角,Fofa固定在页面左上角。 53 | 54 | >url处可以点击直接新建标签页跳转。 55 | 56 | 查询的速度: 57 | 58 | >通过api查询的速度远超某些浏览器插件的速度。 59 | 60 | 安装的快捷: 61 | 62 | >本项目已经公开分享到greasyfork,安装完油猴插件之后,直接搜索即可安装。 63 | 可自动推送更新。 64 | 65 | 66 | ## 快速开始体验 67 | 68 | ### 1. 安装油猴 69 | 70 | 首先浏览器需要安装油猴插件,下面链接写的而很详细。 71 | https://zhuanlan.zhihu.com/p/128453110 72 | 73 | 74 | ### 2. 获取脚本并安装 75 | 76 | 方式一:可直接浏览器搜索然后点击安装即可 77 | 78 | Hunter安装: 79 | 80 | https://greasyfork.org/zh-CN/scripts/440243-hunter-view 81 | 82 | Fofa安装(同理) 83 | 84 | https://greasyfork.org/zh-CN/scripts/440382-fofa-view 85 | 86 | ![](./doc/images/huoqu.png) 87 | 88 | 方式二:你也可以在greasyfork搜索Hunter view或者FOFA view (如果搜索不到,点击显示所有语言的结果) 89 | 90 | 然后直接安装 91 | 92 | 安装完成之后记得在管理面板给它启用 93 | ![](./doc/images/dakai.png) 94 | 95 | ## 使用方法 96 | 97 | 1. 登录 98 | 99 | # !!!第一次用hunter api的同学 记得先重置(刷新)下自己的key 再去登录 100 | 101 | (很多朋友反映第一次使用hunter key的话查不出数据,重置下key就好了 平台的问题) 102 | 103 | (以下使用方法 FOFA view同理 不做赘述) 104 | 105 | 打开任意一个网站,点击登录 106 | 107 | ![](./doc/images/denglu.png) 108 | 109 | 输入Hunter平台的手机号和key值 110 | 111 | ![](./doc/images/shouji.png) 112 | ![](./doc/images/key.png) 113 | 114 | key值在hunter平台的个人中心有展示。 115 | 116 | 登录一次之后即可 其他网站不需要重复登录。 117 | 118 | 2. 使用演示 119 | 120 | 第一次使用需要给权限,这里选总是允许。 121 | 122 | ![](./doc/images/yunxu.png) 123 | 124 | 然后刷新网站,加载脚本 125 | 126 | 之后点击 Hunter或者Fofa 识别结果 127 | 128 | ![](./doc/images/shibie.png) 129 | 130 | 这个时候网站右上角会出现小按钮 131 | 132 | ![](./doc/images/anniu.png) 133 | 134 | 点开按钮 展示数据 135 | 136 | Hunter展示: 137 | 138 | ![](./doc/images/zhanshi.png) 139 | 140 | Fofa展示 141 | 142 | ![](./doc/images/fofa.png) 143 | 144 | 展示界面的url 可以直接点击会新建标签页跳转到指定链接。 145 | 146 | 并且返回来的时候 标签不会收缩,方便继续阅读,想收缩的时候,点击收缩即可。 147 | 148 | 149 | 3. 更新!! 150 | 151 | # 更新得强调下 152 | 153 | 手动更新: 154 | >Hunter view点击这个链接 会提示 https://greasyfork.org/zh-CN/scripts/440243-hunter-view 是否需要更新。 155 | 156 | >Fofa view点击这个链接 会提示 https://greasyfork.org/zh-CN/scripts/440382-fofa-view 是否需要更新。 157 | 158 | 159 | 自动更新(推荐):打开油猴找管理面板 找到hunter_view(Fofa同理) 160 | 161 | ![](./doc/images/gengxin1.png) 162 | ![](./doc/images/gengxin.png) 163 | 164 | 勾选自动更新! 165 | 166 | 167 | 168 | ## 注意事项 169 | 1. Hunter view默认收集的是前六个月的50条资产,(扣除的积分情况是根据查询的数据来定的,一般情况扣不了多少积分) 170 | 171 | 2. Hunter view 每次使用消耗个位数的积分,当然有黑名单机制,你可以点击这里 这样就不会去查询这个网站,建议添加*.baidu.com 等等 172 | 173 | ![](./doc/images/paichu.png) 174 | 175 | 3. Fofa view 搜集的是一年内的50条数据,fofa原本的浏览器插件时搜集全部资产,为了数据的时效性,这里油猴脚本查的是一年内。(所以数据不同不必奇怪) 176 | 177 | 4. !!!记得做好更新措施!!!很多新功能得更新!!!! 178 | 179 | 180 | 181 | 182 | 183 | ## 为 【Space_view】 做贡献 184 | 185 | 【Space_view】 是一个免费且开源的项目,我们欢迎任何人为其开发和进步贡献力量。 186 | 187 | - 在使用过程中出现任何问题,可以通过 issues 来反馈。 188 | - Bug 的修复可以直接提交 Pull Request 到 dev 分支。 189 | - 如果是增加新的功能特性,请先创建一个 issue 并做简单描述以及大致的实现方法,提议被采纳后,就可以创建一个实现新特性的 Pull Request。 190 | - 欢迎对说明文档做出改善,帮助更多的人使用 【Space_view】 191 | - 贡献代码请提交 PR 至 dev 分支,master 分支仅用于发布稳定可用版本。 192 | - 如果你有任何其他方面的问题或合作,欢迎发送邮件至 0x727Team@gmail.com或者2034009618@qq.com本人邮箱。 193 | 194 | > 提醒:和项目相关的问题最好在 issues 中反馈,这样方便其他有类似问题的人可以快速查找解决方法,并且也避免了我们重复回答一些问题。 195 | 196 | ## FOFA共创者计划 197 | Space_view已加入FOFA[共创者计划](https://fofa.info/development),感谢FOFA提供的账号支持。 198 | 199 | ![](./doc/images/fofa.PNG) 200 | -------------------------------------------------------------------------------- /Fofa_view.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name FOFA view 3 | // @namespace http://tampermonkey.net/ 4 | // @version 0.5 5 | // @description Send the current website to FOFA 6 | // @author 0cat 7 | // @match http://*/* 8 | // @match https://*/* 9 | // @grant GM_registerMenuCommand 10 | // @grant GM_xmlhttpRequest 11 | // @grant GM_setValue 12 | // @grant GM_getValue 13 | // @connect * 14 | // @license MIT 15 | // ==/UserScript== 16 | 17 | GM_registerMenuCommand("FOFA Login", openLoginForm, "l"); 18 | GM_registerMenuCommand("FOFA 识别结果", FofaFind, "s"); 19 | 20 | // 创建自定义输入表单 21 | function openLoginForm() { 22 | const body = document.getElementsByTagName('body')[0]; 23 | const loginDiv = document.createElement('div'); 24 | loginDiv.innerHTML = ` 25 |
26 |

Login to FOFA

27 |

30 |

33 | 34 | 35 |
36 | `; 37 | body.appendChild(loginDiv); 38 | 39 | document.getElementById('loginSubmit').onclick = function() { 40 | const username = document.getElementById('fofaUsername').value; 41 | const FofaKey = document.getElementById('fofaKey').value; 42 | if (username && FofaKey) { 43 | GM_setValue("username", username); 44 | GM_setValue("FofaKey", FofaKey); 45 | alert("Login successful!"); 46 | body.removeChild(loginDiv); // Remove form after login 47 | } else { 48 | alert("Please fill in both fields."); 49 | } 50 | }; 51 | 52 | document.getElementById('loginCancel').onclick = function() { 53 | body.removeChild(loginDiv); 54 | }; 55 | } 56 | 57 | // 识别功能 58 | function FofaFind() { 59 | var username = GM_getValue("username"); 60 | var FofaKey = GM_getValue("FofaKey"); 61 | 62 | if (!username || !FofaKey) { 63 | console.error("No username or FofaKey found"); 64 | return; 65 | } 66 | const body = document.getElementsByTagName('body')[0]; 67 | const div = document.createElement('div'); 68 | div.innerHTML = `
69 |
70 | 71 |
隐藏
72 |
73 |
74 |
75 |
76 |
RDAP_IP_CIDR:
77 |
N/A
78 |
79 |
80 |
RDAP_描述:
81 |
N/A
82 |
83 |
84 |
地区:
85 |
null
86 |
87 |
88 |
运营商和org:
89 |
null
90 |
91 |
92 |
ICP:
93 |
null
94 |
95 |
96 |
协议:
97 |
null
98 |
99 |
100 |
端口:
101 |
null
102 |
103 |
104 |
复制
105 | 128 |
129 |
`; 130 | body.appendChild(div); 131 | // 隐藏/展开功能 132 | const hideBtn = document.getElementById('hideBtn'); 133 | const expandBtn = document.getElementById('expandBtn'); 134 | const contentDiv = document.getElementById('contentDiv'); 135 | 136 | hideBtn.onclick = function() { 137 | contentDiv.style.display = 'none'; 138 | hideBtn.style.display = 'none'; 139 | expandBtn.style.display = 'block'; 140 | }; 141 | 142 | expandBtn.onclick = function() { 143 | contentDiv.style.display = 'block'; 144 | hideBtn.style.display = 'block'; 145 | expandBtn.style.display = 'none'; 146 | }; 147 | 148 | var target = window.location.hostname; 149 | var isValidIP_reg = /(\d{1,3}\.){3}\d{1,3}/; 150 | 151 | // 如果是 IP,则执行 RDAP 查询 152 | if (isValidIP_reg.test(target)) { 153 | // 发起 RDAP 请求 154 | var rdap_url = `https://rdap.apnic.net/ip/${target}`; 155 | GM_xmlhttpRequest({ 156 | method: "GET", 157 | url: rdap_url, 158 | onload: function(xhr) { 159 | 160 | if (xhr.status !== 200) { 161 | console.error("RDAP Request failed, status code:", xhr.status); 162 | return; 163 | } 164 | const rdap_res = JSON.parse(xhr.responseText); 165 | // 提取 CIDR 和描述 166 | const handle = rdap_res.handle; 167 | const cidr = rdap_res.cidr0_cidrs[0].v4prefix + "/" + rdap_res.cidr0_cidrs[0].length; 168 | const description = rdap_res.remarks[0].description[0] || 'N/A'; 169 | 170 | // 将 CIDR 和描述信息填充到小卡片中 171 | const rdap_cidr_element = document.getElementsByClassName('rdap_cidr')[0]; 172 | const rdap_description_element = document.getElementsByClassName('rdap_description')[0]; 173 | rdap_cidr_element.textContent = cidr || 'N/A'; 174 | rdap_description_element.textContent = description || 'N/A'; 175 | }, 176 | onerror: function(xhr) { 177 | console.error("RDAP Request error:", xhr); 178 | } 179 | }); 180 | } 181 | 182 | var Fofa_url = "https://fofa.info/api/v1/search/all?email=" + username + "&key=" + FofaKey + "&fields=country,province,city,isp,as_organization,ip,title,protocol,port,host,server,icp&qbase64="; 183 | 184 | var search, url; 185 | 186 | if (isValidIP_reg.test(target)) { 187 | search = btoa('ip=="' + target + '"'); 188 | url = Fofa_url + search; 189 | } else { 190 | search = btoa('host=="' + target + '"'); 191 | url = Fofa_url + search; 192 | } 193 | if (!url) { 194 | console.error("URL generation failed."); 195 | return; 196 | } 197 | 198 | // 发起 FOFA 查询请求 199 | try { 200 | GM_xmlhttpRequest({ 201 | method: "GET", 202 | url: url, 203 | onload: function(xhr) { 204 | 205 | if (xhr.status !== 200) { 206 | console.error("FOFA Request failed, status code:", xhr.status); 207 | return; 208 | } 209 | const res = JSON.parse(xhr.responseText); 210 | 211 | const target_data = res.results || []; 212 | if (target_data.length === 0) { 213 | 214 | return; 215 | } 216 | // 获取 HTML 元素 217 | const area = document.getElementsByClassName('area_fofa')[0]; 218 | const org = document.getElementsByClassName('org_fofa')[0]; 219 | const protocol = document.getElementsByClassName('protocol_fofa')[0]; 220 | const port = document.getElementsByClassName('port_fofa')[0]; 221 | const icp = document.getElementsByClassName('icp_fofa')[0]; 222 | 223 | const table_title = document.getElementsByClassName('table_title_fofa')[0]; 224 | const table_protocol = document.getElementsByClassName('table_protocol_fofa')[0]; 225 | const table_port = document.getElementsByClassName('table_port_fofa')[0]; 226 | const table_server = document.getElementsByClassName('table_server_fofa')[0]; 227 | const table_url = document.getElementsByClassName('table_url_fofa')[0]; 228 | 229 | // 遍历数据并展示到页面 230 | let title_innerHTML = `
标题
`; 231 | let protocol_innerHTML = `
协议
`; 232 | let port_innerHTML = `
端口
`; 233 | let server_innerHTML = `
server
`; 234 | let url_innerHTML = `
url
`; 235 | 236 | target_data.forEach(item => { 237 | title_innerHTML += `
${item[6] || 'N/A'}
`; 238 | protocol_innerHTML += `
${item[7] || 'N/A'}
`; 239 | if (item[7] === "http" || item[7] === "https") { 240 | var urlNoProtocol = item[9].replace(/^https?\:\/\//i, ""); 241 | var urlProtocol = item[7] + "://" + urlNoProtocol; 242 | url_innerHTML += `
${urlProtocol}
`; 243 | } else { 244 | url_innerHTML += `
`; 245 | } 246 | port_innerHTML += `
${item[8] || 'N/A'}
`; 247 | server_innerHTML += `
${item[10] || 'N/A'}
`; 248 | }); 249 | 250 | table_title.innerHTML = title_innerHTML; 251 | table_protocol.innerHTML = protocol_innerHTML; 252 | table_port.innerHTML = port_innerHTML; 253 | table_server.innerHTML = server_innerHTML; 254 | table_url.innerHTML = url_innerHTML; 255 | const target_location = target_data[0] || {}; 256 | area.textContent = [target_location[0] || '', target_location[1] || '', target_location[2] || ''].filter(item => item).join('-') || 'N/A'; 257 | org.textContent = [target_location[3] || '', target_location[4] || ''].filter(item => item).join(',') || 'N/A'; 258 | icp.textContent = Array.from(new Set(target_data.filter(item => item[11]).map(item => item[11]))).join(',') || 'N/A'; 259 | protocol.textContent = Array.from(new Set(target_data.filter(item => item[7]).map(item => item[7]))).join(',') || 'N/A'; 260 | port.textContent = Array.from(new Set(target_data.filter(item => item[8]).map(item => item[8]))).join(',') || 'N/A'; 261 | }, 262 | onerror: function(xhr) { 263 | console.error("FOFA Request error:", xhr); 264 | } 265 | }); 266 | } catch (e) { 267 | console.error("Error while sending request:", e); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /Hunter_view.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Hunter view 3 | // @namespace http://tampermonkey.net/ 4 | // @version 0.5 5 | // @description Send the current website to Hunter 6 | // @author 0cat 7 | // @match http://*/* 8 | // @match https://*/* 9 | // @grant GM_registerMenuCommand 10 | // @grant GM_xmlhttpRequest 11 | // @grant GM_setValue 12 | // @grant GM_getValue 13 | // @connect * 14 | // @license MIT 15 | // ==/UserScript== 16 | 17 | GM_registerMenuCommand("Hunter Login", openLoginForm, "l"); 18 | GM_registerMenuCommand("Hunter 识别结果", HunterFind, "s"); 19 | 20 | // 创建自定义输入表单 21 | function openLoginForm() { 22 | const body = document.getElementsByTagName('body')[0]; 23 | const loginDiv = document.createElement('div'); 24 | loginDiv.innerHTML = ` 25 |
26 |

Login to Hunter

27 |

30 |

33 | 34 | 35 |
36 | `; 37 | body.appendChild(loginDiv); 38 | 39 | document.getElementById('loginSubmit').onclick = function() { 40 | const username = document.getElementById('hunterUsername').value; 41 | const HunterKey = document.getElementById('hunterKey').value; 42 | if (username && HunterKey) { 43 | GM_setValue("username", username); 44 | GM_setValue("HunterKey", HunterKey); 45 | alert("Login successful!"); 46 | body.removeChild(loginDiv); // Remove form after login 47 | } else { 48 | alert("Please fill in both fields."); 49 | } 50 | }; 51 | 52 | document.getElementById('loginCancel').onclick = function() { 53 | body.removeChild(loginDiv); 54 | }; 55 | } 56 | 57 | function HunterFind() { 58 | var username = GM_getValue("username"); 59 | var HunterKey = GM_getValue("HunterKey"); 60 | 61 | if (!username || !HunterKey) { 62 | console.error("No username or HunterKey found"); 63 | return; 64 | } 65 | 66 | const body = document.getElementsByTagName('body')[0]; 67 | const div = document.createElement('div'); 68 | div.innerHTML = `
69 |
70 | 71 |
隐藏
72 |
73 |
74 |
75 |
76 |
RDAP_IP_CIDR:
77 |
N/A
78 |
79 |
80 |
RDAP_描述:
81 |
N/A
82 |
83 |
84 |
地区:
85 |
null
86 |
87 |
88 |
运营商和org:
89 |
null
90 |
91 |
92 |
ICP:
93 |
null
94 |
95 |
96 |
协议:
97 |
null
98 |
99 |
100 |
端口:
101 |
null
102 |
103 |
104 |
剩余积分:
105 |
null
106 |
107 |
108 |
109 | 复制 110 |
111 | 134 |
135 |
`; 136 | body.appendChild(div); 137 | // 隐藏/展开功能 138 | const hideBtn = document.getElementById('hideBtn'); 139 | const expandBtn = document.getElementById('expandBtn'); 140 | const contentDiv = document.getElementById('contentDiv'); 141 | 142 | hideBtn.onclick = function() { 143 | contentDiv.style.display = 'none'; 144 | hideBtn.style.display = 'none'; 145 | expandBtn.style.display = 'block'; 146 | }; 147 | 148 | expandBtn.onclick = function() { 149 | contentDiv.style.display = 'block'; 150 | hideBtn.style.display = 'block'; 151 | expandBtn.style.display = 'none'; 152 | }; 153 | 154 | var target = window.location.hostname; 155 | 156 | var isValidIP_reg = /(\d{1,3}\.){3}\d{1,3}/; 157 | 158 | // 如果是 IP,则执行 RDAP 查询 159 | if (isValidIP_reg.test(target)) { 160 | // 发起 RDAP 请求 161 | var rdap_url = `https://rdap.apnic.net/ip/${target}`; 162 | GM_xmlhttpRequest({ 163 | method: "GET", 164 | url: rdap_url, 165 | onload: function(xhr) { 166 | if (xhr.status !== 200) { 167 | console.error("RDAP Request failed, status code:", xhr.status); 168 | return; 169 | } 170 | const rdap_res = JSON.parse(xhr.responseText); 171 | 172 | // 提取 CIDR 和描述 173 | const handle = rdap_res.handle; 174 | const cidr = rdap_res.cidr0_cidrs[0].v4prefix + "/" + rdap_res.cidr0_cidrs[0].length; 175 | const description = rdap_res.remarks[0].description[0] || 'N/A'; 176 | 177 | // 将 CIDR 和描述信息填充到小卡片中 178 | const rdap_cidr_element = document.getElementsByClassName('rdap_cidr')[0]; 179 | const rdap_description_element = document.getElementsByClassName('rdap_description')[0]; 180 | rdap_cidr_element.textContent = cidr || 'N/A'; 181 | rdap_description_element.textContent = description || 'N/A'; 182 | 183 | }, 184 | onerror: function(xhr) { 185 | console.error("RDAP Request error:", xhr); 186 | } 187 | }); 188 | } 189 | 190 | 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="; 191 | 192 | var search, url; 193 | 194 | if (isValidIP_reg.test(target)) { 195 | search = btoa('ip=="' + target + '"'); 196 | url = Hunter_url + search; 197 | } else { 198 | search = btoa('domain=="' + target + '"'); 199 | url = Hunter_url + search; 200 | } 201 | 202 | if (!url) { 203 | console.error("URL generation failed."); 204 | return; 205 | } 206 | 207 | // 发起 Hunter 查询请求 208 | try { 209 | GM_xmlhttpRequest({ 210 | method: "GET", 211 | url: url, 212 | onload: function(xhr) { 213 | if (xhr.status !== 200) { 214 | console.error("Hunter Request failed, status code:", xhr.status); 215 | return; 216 | } 217 | const res = JSON.parse(xhr.responseText); 218 | 219 | const target_data = res.data?.arr || []; 220 | if (target_data.length === 0) { 221 | return; 222 | } 223 | 224 | // 获取 HTML 元素 225 | const area = document.getElementsByClassName('area')[0]; 226 | const org = document.getElementsByClassName('org')[0]; 227 | const protocol = document.getElementsByClassName('protocol')[0]; 228 | const port = document.getElementsByClassName('port')[0]; 229 | const surplus = document.getElementsByClassName('surplus')[0]; 230 | const icpnumber = document.getElementsByClassName('icpnumber')[0]; 231 | 232 | const table_title = document.getElementsByClassName('table_title')[0]; 233 | const table_protocol = document.getElementsByClassName('table_protocol')[0]; 234 | const table_port = document.getElementsByClassName('table_port')[0]; 235 | const table_code = document.getElementsByClassName('table_code')[0]; 236 | const table_url = document.getElementsByClassName('table_url')[0]; 237 | 238 | // 遍历数据并展示到页面 239 | let title_innerHTML = `
标题
`; 240 | let protocol_innerHTML = `
协议
`; 241 | let port_innerHTML = `
端口
`; 242 | let code_innerHTML = `
状态码
`; 243 | let url_innerHTML = `
url
`; 244 | 245 | target_data.forEach(item => { 246 | title_innerHTML += `
${item.web_title || 'N/A'}
`; 247 | protocol_innerHTML += `
${item.protocol || 'N/A'}
`; 248 | code_innerHTML += `
${item.status_code || 'N/A'}
`; 249 | url_innerHTML += `
${item.url}
`; 250 | port_innerHTML += `
${item.port || 'N/A'}
`; 251 | }); 252 | 253 | table_title.innerHTML = title_innerHTML; 254 | table_protocol.innerHTML = protocol_innerHTML; 255 | table_code.innerHTML = code_innerHTML; 256 | table_port.innerHTML = port_innerHTML; 257 | table_url.innerHTML = url_innerHTML; 258 | 259 | const target_location = target_data[0] || {}; 260 | area.textContent = [target_location.country || '', target_location.province || '', target_location.city || ''].filter(item => item).join('-') || 'N/A'; 261 | org.textContent = Array.from(new Set(target_data.filter(item => item.as_org).map(item => item.as_org))).join(',') || 'N/A'; 262 | icpnumber.textContent = target_data[0].number || 'N/A'; 263 | protocol.textContent = Array.from(new Set(target_data.filter(item => item.protocol).map(item => item.protocol))).join(',') || 'N/A'; 264 | port.textContent = Array.from(new Set(target_data.filter(item => item.port).map(item => item.port))).join(',') || 'N/A'; 265 | surplus.textContent = res.data.rest_quota?.split(':')[1] || 'N/A'; 266 | 267 | }, 268 | onerror: function(xhr) { 269 | console.error("Hunter Request error:", xhr); 270 | } 271 | }); 272 | } catch (e) { 273 | console.error("Error while sending request:", e); 274 | } 275 | } 276 | 277 | function getNewDate(flag, many) { 278 | const thirtyDays = [4, 6, 9, 11]; 279 | const thirtyOneDays = [1, 3, 5, 7, 8, 10, 12]; 280 | const currDate = new Date(); 281 | const year = currDate.getFullYear(); 282 | let month = currDate.getMonth() + 1; 283 | let countDays = 0; 284 | let targetDateMilli = 0; 285 | let GMTDate = ''; 286 | let targetYear = ''; 287 | let targetMonth = ''; 288 | let targetDate = ''; 289 | let dealDate = ''; 290 | let hh = currDate.getHours() < 10 ? "0" + currDate.getHours() : currDate.getHours(); 291 | let mm = currDate.getMinutes() < 10 ? "0" + currDate.getMinutes() : currDate.getMinutes(); 292 | let ss = currDate.getSeconds() < 10 ? "0" + currDate.getSeconds() : currDate.getSeconds(); 293 | 294 | const isLeapYear = !!((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); 295 | 296 | for (let i = 0; i < many; i++) { 297 | if (flag === 'before') { 298 | month = month - 1 <= 0 ? 12 : month - 1; 299 | } else { 300 | month = month + 1 > 12 ? 1 : month + 1; 301 | } 302 | thirtyDays.includes(month) ? (countDays += 30) : thirtyOneDays.includes(month) ? (countDays += 31) : isLeapYear ? (countDays += 29) : (countDays += 28); 303 | } 304 | 305 | targetDateMilli = currDate.setDate(currDate.getDate() - (flag === 'before' ? countDays : countDays * -1)); 306 | GMTDate = new Date(targetDateMilli); 307 | targetYear = GMTDate.getFullYear(); 308 | targetMonth = GMTDate.getMonth() + 1; 309 | targetDate = GMTDate.getDate(); 310 | targetMonth = targetMonth.toString().padStart(2, '0'); 311 | targetDate = targetDate.toString().padStart(2, '0'); 312 | dealDate = `${targetYear}-${targetMonth}-${targetDate} ${hh}:${mm}:${ss}`; 313 | return escape(dealDate); 314 | } 315 | --------------------------------------------------------------------------------