├── 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 | 
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 | 
87 |
88 | 方式二:你也可以在greasyfork搜索Hunter view或者FOFA view (如果搜索不到,点击显示所有语言的结果)
89 |
90 | 然后直接安装
91 |
92 | 安装完成之后记得在管理面板给它启用
93 | 
94 |
95 | ## 使用方法
96 |
97 | 1. 登录
98 |
99 | # !!!第一次用hunter api的同学 记得先重置(刷新)下自己的key 再去登录
100 |
101 | (很多朋友反映第一次使用hunter key的话查不出数据,重置下key就好了 平台的问题)
102 |
103 | (以下使用方法 FOFA view同理 不做赘述)
104 |
105 | 打开任意一个网站,点击登录
106 |
107 | 
108 |
109 | 输入Hunter平台的手机号和key值
110 |
111 | 
112 | 
113 |
114 | key值在hunter平台的个人中心有展示。
115 |
116 | 登录一次之后即可 其他网站不需要重复登录。
117 |
118 | 2. 使用演示
119 |
120 | 第一次使用需要给权限,这里选总是允许。
121 |
122 | 
123 |
124 | 然后刷新网站,加载脚本
125 |
126 | 之后点击 Hunter或者Fofa 识别结果
127 |
128 | 
129 |
130 | 这个时候网站右上角会出现小按钮
131 |
132 | 
133 |
134 | 点开按钮 展示数据
135 |
136 | Hunter展示:
137 |
138 | 
139 |
140 | Fofa展示
141 |
142 | 
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 | 
162 | 
163 |
164 | 勾选自动更新!
165 |
166 |
167 |
168 | ## 注意事项
169 | 1. Hunter view默认收集的是前六个月的50条资产,(扣除的积分情况是根据查询的数据来定的,一般情况扣不了多少积分)
170 |
171 | 2. Hunter view 每次使用消耗个位数的积分,当然有黑名单机制,你可以点击这里 这样就不会去查询这个网站,建议添加*.baidu.com 等等
172 |
173 | 
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 | 
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 |
73 |
74 |
75 |
76 |
RDAP_IP_CIDR:
77 |
N/A
78 |
79 |
80 |
RDAP_描述:
81 |
N/A
82 |
83 |
87 |
88 |
运营商和org:
89 |
null
90 |
91 |
95 |
99 |
100 |
端口:
101 |
null
102 |
103 |
104 |
复制
105 |
106 |
107 |
110 |
113 |
116 |
119 |
126 |
127 |
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 += ``;
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 |
Hunter展开
71 |
隐藏
72 |
73 |
74 |
75 |
76 |
RDAP_IP_CIDR:
77 |
N/A
78 |
79 |
80 |
RDAP_描述:
81 |
N/A
82 |
83 |
87 |
88 |
运营商和org:
89 |
null
90 |
91 |
95 |
99 |
100 |
端口:
101 |
null
102 |
103 |
104 |
剩余积分:
105 |
null
106 |
107 |
108 |
109 | 复制
110 |
111 |
112 |
113 |
116 |
119 |
122 |
125 |
132 |
133 |
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 += ``;
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 |
--------------------------------------------------------------------------------