Extract IP:port and domains with dark themed tabbed interface
// ==UserScript==
// @name FOFA IP and Domain Extractor (Dark Tabbed)
// @namespace http://tampermonkey.net/
// @version 1.6
// @description Extract IP:port and domains with dark themed tabbed interface
// @author zephyrus
// @match https://fofa.info/result*
// @match https://*.fofa.info/result*
// @grant GM_setClipboard
// @grant GM_addStyle
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// Add custom CSS styles
GM_addStyle(`
.fofa-extractor-container {
margin: 20px auto;
font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
width: 70%;
max-width: 800px;
}
.fofa-tab-container {
display: flex;
flex-direction: column;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
background: #2d2d2d;
}
.fofa-tab-header {
display: flex;
background: #1e1e1e;
border-bottom: 1px solid #3a3a3a;
}
.fofa-tab {
padding: 10px 20px;
cursor: pointer;
font-weight: 500;
color: #aaa;
transition: all 0.3s;
border-right: 1px solid #3a3a3a;
position: relative;
}
.fofa-tab:last-child {
border-right: none;
}
.fofa-tab.active {
color: #05f2f2;
background: #2d2d2d;
}
.fofa-tab.active:after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
right: 0;
height: 2px;
background: #05f2f2;
}
.fofa-tab-badge {
display: inline-block;
padding: 2px 6px;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
font-size: 12px;
margin-left: 8px;
color: #ddd;
}
.fofa-tab-content {
display: none;
background: #2d2d2d;
padding: 15px;
}
.fofa-tab-content.active {
display: block;
}
.fofa-content-box {
max-height: 400px;
overflow-y: auto;
padding: 12px;
background: #252525;
border-radius: 4px;
line-height: 1.6;
white-space: pre;
font-size: 13px;
color: #e0e0e0;
border: 1px solid #3a3a3a;
font-family: 'Consolas', 'Monaco', monospace;
}
.fofa-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
color: #aaa;
font-size: 13px;
}
.fofa-copy-btn {
padding: 6px 14px;
background: #05f2f2;
color: #111;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
transition: all 0.2s;
font-weight: 500;
}
.fofa-copy-btn:hover {
background: #05e0e0;
}
.fofa-copy-btn.copied {
background: #05a2a2;
color: #fff;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: #1e1e1e;
}
::-webkit-scrollbar-thumb {
background: #05f2f2;
border-radius: 4px;
}
@media (max-width: 768px) {
.fofa-extractor-container {
width: 90%;
}
.fofa-tab-header {
flex-wrap: wrap;
}
.fofa-tab {
flex: 1 0 auto;
text-align: center;
padding: 8px 12px;
}
}
`);
const extractData = () => {
const items = Array.from(document.querySelectorAll(".hsxa-meta-data-item"));
const domains = new Set();
const ips = new Set();
items.forEach(item => {
// Extract domain with port
const domainLink = item.querySelector(".hsxa-host a");
const domain = domainLink?.textContent.trim().replace(/^https?:\/\//, "");
const portElement = item.querySelector(".hsxa-port");
const port = portElement ? atob(portElement.href.match(/qbase64=([^&]+)/)?.[1]).match(/port="([^"]+)"/)?.[1] : "";
if (domain && port) {
domains.add(`${domain}:${port}`);
}
// Extract IP with port
const ipElement = item.querySelector(".hsxa-jump-a");
const ip = ipElement ? atob(ipElement.href.match(/qbase64=([^&]+)/)?.[1]).match(/ip="([^"]+)"/)?.[1] : "";
if (ip && port) {
ips.add(`${ip}:${port}`);
}
});
return {
domains: Array.from(domains).sort(),
ips: Array.from(ips).sort()
};
};
const createTabbedInterface = (ips, domains) => {
const container = document.createElement('div');
container.className = 'fofa-tab-container';
// Create tab headers
const tabHeader = document.createElement('div');
tabHeader.className = 'fofa-tab-header';
const ipTab = document.createElement('div');
ipTab.className = 'fofa-tab active';
ipTab.dataset.tab = 'ip';
ipTab.innerHTML = `IP列表 <span class="fofa-tab-badge">${ips.length}</span>`;
const domainTab = document.createElement('div');
domainTab.className = 'fofa-tab';
domainTab.dataset.tab = 'domain';
domainTab.innerHTML = `域名列表 <span class="fofa-tab-badge">${domains.length}</span>`;
tabHeader.appendChild(ipTab);
tabHeader.appendChild(domainTab);
container.appendChild(tabHeader);
// Create tab contents
const contentContainer = document.createElement('div');
contentContainer.className = 'fofa-tab-contents';
// IP Tab Content
const ipContent = document.createElement('div');
ipContent.className = 'fofa-tab-content active';
ipContent.id = 'ip-tab';
const ipToolbar = document.createElement('div');
ipToolbar.className = 'fofa-toolbar';
ipToolbar.innerHTML = '<div>共 ' + ips.length + ' 个IP</div>';
const ipCopyBtn = document.createElement('button');
ipCopyBtn.className = 'fofa-copy-btn';
ipCopyBtn.textContent = '复制所有IP';
ipCopyBtn.onclick = () => {
GM_setClipboard(ips.join('\n'));
ipCopyBtn.textContent = '已复制!';
ipCopyBtn.classList.add('copied');
setTimeout(() => {
ipCopyBtn.textContent = '复制所有IP';
ipCopyBtn.classList.remove('copied');
}, 2000);
};
ipToolbar.appendChild(ipCopyBtn);
ipContent.appendChild(ipToolbar);
const ipContentBox = document.createElement('div');
ipContentBox.className = 'fofa-content-box';
ipContentBox.textContent = ips.join('\n');
ipContent.appendChild(ipContentBox);
// Domain Tab Content
const domainContent = document.createElement('div');
domainContent.className = 'fofa-tab-content';
domainContent.id = 'domain-tab';
const domainToolbar = document.createElement('div');
domainToolbar.className = 'fofa-toolbar';
domainToolbar.innerHTML = '<div>共 ' + domains.length + ' 个域名</div>';
const domainCopyBtn = document.createElement('button');
domainCopyBtn.className = 'fofa-copy-btn';
domainCopyBtn.textContent = '复制所有域名';
domainCopyBtn.onclick = () => {
GM_setClipboard(domains.join('\n'));
domainCopyBtn.textContent = '已复制!';
domainCopyBtn.classList.add('copied');
setTimeout(() => {
domainCopyBtn.textContent = '复制所有域名';
domainCopyBtn.classList.remove('copied');
}, 2000);
};
domainToolbar.appendChild(domainCopyBtn);
domainContent.appendChild(domainToolbar);
const domainContentBox = document.createElement('div');
domainContentBox.className = 'fofa-content-box';
domainContentBox.textContent = domains.join('\n');
domainContent.appendChild(domainContentBox);
contentContainer.appendChild(ipContent);
contentContainer.appendChild(domainContent);
container.appendChild(contentContainer);
// Add tab switching functionality
const tabs = container.querySelectorAll('.fofa-tab');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
// Update active tab
tabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
// Update active content
const tabName = tab.dataset.tab;
container.querySelectorAll('.fofa-tab-content').forEach(content => {
content.classList.remove('active');
});
container.querySelector(`#${tabName}-tab`).classList.add('active');
});
});
return container;
};
const insertResults = () => {
// Check if already inserted
if (document.getElementById('fofa-extractor-container')) return;
const targetElement = document.querySelector('div.contentContainer.resultIndex > div:nth-child(1) > div.relatedSearch');
if (!targetElement) return;
const { domains, ips } = extractData();
if (domains.length === 0 && ips.length === 0) return;
const container = document.createElement('div');
container.id = 'fofa-extractor-container';
container.className = 'fofa-extractor-container';
container.appendChild(createTabbedInterface(ips, domains));
targetElement.parentNode.insertBefore(container, targetElement);
};
// Use MutationObserver to wait for page load
const observer = new MutationObserver((mutations, obs) => {
if (document.querySelector('.hsxa-meta-data-item')) {
obs.disconnect();
setTimeout(insertResults, 500);
}
});
observer.observe(document, {
childList: true,
subtree: true
});
// Also run when page updates (e.g. pagination)
document.addEventListener('DOMNodeInserted', insertResults);
})();