Files
Zeus-Grabber-Update-Tool-2026/Main.py
T
2026-08-27 11:04:16 -06:00

1346 lines
50 KiB
Python

import sys
from PyQt6.QtCore import Qt, QSize, QPropertyAnimation, QThread, pyqtSignal, QDir, QTimer
from PyQt6.QtGui import QFont, QPixmap, QIcon, QFontDatabase
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton, QFileDialog, QPlainTextEdit,
QHBoxLayout, QStackedWidget, QLabel, QFrame, QGridLayout, QRadioButton, QButtonGroup, QGroupBox, QSizePolicy, QTextEdit, QLineEdit, QSpinBox, QMessageBox
)
import requests, time, urllib3, socket
import random, os, re
from bs4 import BeautifulSoup
from urllib.parse import urlparse
# Disable insecure request warnings
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.common.by import By
def resource_path(relative_path):
try:
base_path = sys._MEIPASS
except AttributeError:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
# Construct an absolute path to the Zeus_Result folder in the user's home directory
current_dir = QDir.currentPath() + "/Zeus_Result"
# Ensure the directory exists
QDir(current_dir).mkpath(".")
try:
os.mkdir('Zeus_Result')
except:
pass
user_agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.92 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:115.0) Gecko/20100101 Firefox/115.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.92 Safari/537.36 Edg/117.0.2045.60",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15",
"Mozilla/5.0 (Linux; Android 13; Pixel 6 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.92 Mobile Safari/537.36",
]
def button_start():
return """
QPushButton {
background-color: #8A2BE2;
color: white;
font-size: 16px;
border: none;
}
QPushButton:hover {
background-color: #1ABC9C;
}
QPushButton:pressed {
background-color: cyan;
}
"""
def input_style():
return """
QLineEdit {
background-color: #2C3E50;
color: #ECF0F1;
font-size: 14px;
padding: 10px;
border: 2px solid #1ABC9C;
border-radius: 10px;
}
QLineEdit:focus {
border: 2px solid #16A085;
}
"""
def button_style():
return """
QPushButton {
background-color: #1ABC9C;
color: white;
font-size: 16px;
border: none;
padding: 10px;
border-radius: 10px;
}
QPushButton:hover {
background-color: #16A085;
}
QPushButton:pressed {
background-color: #1ABC9C;
}
"""
def Editor_TXTStyle():
return """
QTextEdit {
background-color: #2C3E50;
color: #ECF0F1;
border: 2px solid #1ABC9C;
padding: 10px;
font-size: 14px;
border-radius: 10px;
}
QScrollBar:vertical {
border: none;
background: rgb(52, 73, 94);
width: 14px;
margin: 15px 0 15px 0;
border-radius: 7px;
}
QScrollBar::handle:vertical {
background-color: rgb(26, 188, 156);
min-height: 30px;
border-radius: 7px;
}
QScrollBar::handle:vertical:hover {
background-color: #1ABC9C;
}
QScrollBar::handle:vertical:pressed {
background-color: #16A085;
}
QScrollBar::sub-line:vertical {
height: 0px;
}
QScrollBar::add-line:vertical {
height: 0px;
}
QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical {
width: 0px;
height: 0px;
}
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none;
}
"""
def read_list(file_path):
"""Read the IP list from the given file and return it as a list of strings."""
if not os.path.exists(file_path):
#print(f"Error: The file {file_path} does not exist.")
return []
with open(file_path, 'r') as file:
content = file.read().splitlines()
return content
def robtex(ip):
try:
response = requests.get(f'https://freeapi.robtex.com/ipquery/{ip}', verify=False, timeout=10).json()
domains = []
if response['status'] == 'ok':
for value in response.values():
try:
for item in value:
domains.append(item['o'])
except TypeError:
continue
return domains #if domains else None
except requests.exceptions.RequestException:
return None
except:
return None
"""
Example Qthread
https://doc.qt.io/qtforpython-6/PySide6/QtCore/QThread.html
https://sihabsahariar.medium.com/a-comprehensive-pyqt5-tutorial-on-qthread-and-qthreadpool-66aa5b768496
https://realpython.com/python-pyqt-qthread/#:~:text=In%20PyQt%2C%20you%20use%20QThread,the%20thread%20as%20an%20argument.
class WorkerThread(QThread):
Q_OBJECT
def run():
result = QString()
/* ... here is the expensive or blocking operation ... */
resultReady.emit(result)
# signals
def resultReady(s):
def startWorkInAThread(self):
workerThread = WorkerThread(self)
workerThread.resultReady.connect(self.handleResults)
workerThread.finished.connect(workerThread.deleteLater)
workerThread.start()
"""
class DomainCheckerWorker(QThread):
# Signal to update the console with responsed result
progress = pyqtSignal(str)
def __init__(self, ip_list, save_path, selected_api):
super().__init__()
self.ip_list = ip_list
self.save_path = save_path
#print(selected_api) == 'API #1' if selected will print
self.selected_api = selected_api
self.unique_domains = set() # To store unique domains
def handle_rate_limiting(self, retry_count):
wait_time = 2 ** retry_count
time.sleep(wait_time)
def reverse_ip_lookup(self, ip, session, retry_count=0):
url = f"https://rapiddns.io/sameip/{ip}?full=1&t=None#result"
headers = {"User-Agent": random.choice(user_agents)}
try:
response = session.get(url, headers=headers, verify=False, timeout=10)
if response.status_code == 429: # 429 is status code of many requests in server
if retry_count >= 5:
return None # Done after Retrying after 5 times
else:
self.handle_rate_limiting(retry_count)
return self.reverse_ip_lookup(ip, session, retry_count + 1) # Count per time
soup = BeautifulSoup(response.text, 'html.parser')
domains = []
if soup.find('tbody'):
for row in soup.find('tbody').find_all('tr'):
domain = row.find_all('td')[0].text.strip()
domains.append(domain)
return domains
except:
return None
def run(self):
with requests.Session() as session:
for ip in self.ip_list:
time.sleep(random.uniform(1, 3))
domains = []
if self.selected_api == "API #1":
domains = self.reverse_ip_lookup(ip, session)
elif self.selected_api == "API #2":
domains = robtex(ip)
elif self.selected_api == "API #BOTH":
domains_1 = self.reverse_ip_lookup(ip, session)
domains_2 = []
if not domains_1:
domains_2 = robtex(ip)
domains = (domains_1 or []) + (domains_2 or [])
if domains:
try:
self.unique_domains.update(domains) # Add Domain update with Set
self.progress.emit(f"{ip} Domains Founded : {len(domains)}")
with open(self.save_path, 'a') as file:
for domain in domains:
if '*.' in str(domain):
domain = str(domain).split('*.')[1]
#Emit Domain and WriteFile
self.progress.emit(f"{domain}")
file.write(f"{domain}\n")
except:
pass
#No found domain or IP has no domain
else:
self.progress.emit(f"{ip} : IP has no Domain")
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.result_data = "" #Res Stored
self.ip_list = [] # ListIP from selected file
self.save_path = ""
self.setWindowTitle("Zeus Grabber v1.0 | By DRCrypter.ru")
self.setWindowIcon(QIcon(resource_path('Files/icon1.png')))
self.setGeometry(200, 100, 1000, 600)
# Main widget with layout
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QHBoxLayout(central_widget)
# Dark theme
self.setStyleSheet("""
QWidget {
background-color: #1C1C1C;
color: white;
}
""")
# Sidebar Config
self.sidebar_expanded = True
self.sidebar_width = 180
self.collapsed_sidebar_width = 60
# Side_Frame with border_radius
self.sidebar = QFrame()
self.sidebar.setStyleSheet("""
QFrame {
background-color: #2C3E50;
border-radius: 9px;
}
""")
self.sidebar.setFixedWidth(self.sidebar_width)
self.sidebar_layout = QVBoxLayout(self.sidebar)
self.sidebar_layout.setSpacing(0)
self.sidebar_layout.setContentsMargins(0, 0, 0, 0)
# Logo sidebar
self.logo = QLabel()
self.logo.setFixedHeight(120)
self.logo.setAlignment(Qt.AlignmentFlag.AlignCenter)
pixmap = QPixmap(resource_path("Files/logo.png"))
self.logo.mousePressEvent = self.toggle_sidebar # Add click event
self.logo.setPixmap(pixmap.scaled(90, 120, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation))
self.sidebar_layout.addWidget(self.logo)
# sidebar_button
sidebar_button = """
QPushButton {
background-color: #34495E;
color: white;
font-size: 14px;
border: none;
text-align: left;
padding-left: 10px;
}
QPushButton:hover {
background-color: #1ABC9C;
}
QPushButton:pressed {
background-color: #16A085;
}
"""
# Sidebar buttons
titles = ["Reverse IP", "Generate IP", "Google", "Zone-H"]
self.page_buttons = []
self.icon_size = QSize(24, 24)
for i, title in enumerate(titles):
button = QPushButton(title)
button.setStyleSheet(sidebar_button)
button.setFixedHeight(50)
button.setIcon(QIcon(resource_path(f"Files/icon{i+1}.png")))
button.setIconSize(self.icon_size)
button.setCursor(Qt.CursorShape.PointingHandCursor)
button.clicked.connect(lambda checked, idx=i: self.animate_page_transition(idx))
self.page_buttons.append(button)
self.sidebar_layout.addWidget(button)
# Add stretch Push_Button to the top
self.sidebar_layout.addStretch()
# Sidebar Main_layout
main_layout.addWidget(self.sidebar)
#https://stackoverflow.com/questions/52596386/slide-qstackedwidget-page
# Pages container (QStackedWidget)
self.pages = QStackedWidget()
self.pages.setStyleSheet("background-color: None; border-radius: 10px;")
main_layout.addWidget(self.pages)
# AddPages 2 QStackedWidget
self.pages.addWidget(self.ReverseIP_Page0())
self.pages.addWidget(DomainToIPPage())
self.pages.addWidget(Google_Grabber())
self.pages.addWidget(ZoneH_Grabber())
def ReverseIP_Page0(self):
page = QWidget()
layout = QVBoxLayout(page)
# TopLayer_BTN
top_layout = QGridLayout()
# File_Selection_BTN with Hover Effects
self.file_button = QPushButton("Select *.TXT")
self.file_button.setFixedSize(150, 40)
self.file_button.setStyleSheet("""
QPushButton {
background-color: #16A085;
color: white;
font-size: 16px;
border: none;
}
QPushButton:hover {
background-color: #1ABC9C;
}
QPushButton:pressed {
background-color: #16A085;
}
""")
self.file_button.clicked.connect(self.select_file)
top_layout.addWidget(self.file_button, 0, 0, Qt.AlignmentFlag.AlignLeft)
# Radio_BTN for API selection
self.api_group = QButtonGroup()
self.api1_radio = QRadioButton("API #1")
self.api1_radio.setChecked(True)
self.api1_radio.setStyleSheet("font-size: 14px; color: white;")
self.api_group.addButton(self.api1_radio)
self.api2_radio = QRadioButton("API #2")
self.api2_radio.setStyleSheet("font-size: 14px; color: white;")
self.api_group.addButton(self.api2_radio)
self.api_both_radio = QRadioButton("API #BOTH")
self.api_both_radio.setStyleSheet("font-size: 14px; color: white;")
self.api_group.addButton(self.api_both_radio)
# Add_Radio_BTN to my Layout
top_layout.addWidget(self.api1_radio, 0, 1)
top_layout.addWidget(self.api2_radio, 0, 2)
top_layout.addWidget(self.api_both_radio, 0, 3)
layout.addLayout(top_layout)
# Console area
#thank info with scollbar custom modern ui : https://github.com/Wanderson-Magalhaes/Custom_ScrollBar_QtDesigner/blob/main/Scroll-Bar-Custom.ui
self.console_area = QPlainTextEdit()
self.console_area.setFont(QFont("Arial", 12))
self.console_area.setPlaceholderText("Console Log")
self.console_area.setStyleSheet("""
QPlainTextEdit {
background-color: #34495E; /* Background color for the text area */
border: 2px solid #1ABC9C; /* Border for the text area */
padding: 10px;
color: white;
border-radius: 10px; /* Rounded corners for the text area */
}
QScrollBar:vertical {
border: none;
background: rgb(52, 73, 94);
width: 14px;
margin: 15px 0 15px 0;
border-radius: 7px; /* Rounded corners for the scrollbar background */
}
/* HANDLE BAR VERTICAL */
QScrollBar::handle:vertical {
background-color: rgb(26, 188, 156);
min-height: 30px;
border-radius: 7px; /* Rounded corners for the handle */
}
QScrollBar::handle:vertical:hover {
background-color: #1ABC9C;
}
QScrollBar::handle:vertical:pressed {
background-color: #16A085;
}
/* BTN TOP - SCROLLBAR */
QScrollBar::sub-line:vertical {
height: 0px;
}
/* BTN BOTTOM - SCROLLBAR */
QScrollBar::add-line:vertical {
height: 0px;
}
/* RESET ARROW */
QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical {
width: 0px;
height: 0px;
}
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none;
}
""")
self.console_area.setReadOnly(True) # Allow_USER Permission Read_Only
layout.addWidget(self.console_area)
# START and SAVE
save_start_layout = QHBoxLayout()
# Start_BTN
self.start_button = QPushButton("Start")
self.start_button.setFixedSize(150, 40)
self.start_button.setStyleSheet(button_start())
self.start_button.setEnabled(False)
self.start_button.clicked.connect(self.START_Rev)
save_start_layout.addWidget(self.start_button, Qt.AlignmentFlag.AlignRight)
# Save_BTN
self.save_button = QPushButton("Save *.TXT")
self.save_button.setFixedSize(150, 40)
self.save_button.setStyleSheet("""
QPushButton {
background-color: #1ABC9C;
color: white;
font-size: 16px;
border: none;
}
QPushButton:hover {
background-color: #16A085;
}
QPushButton:pressed {
background-color: #1ABC9C;
}
""")
self.save_button.clicked.connect(self.SAVE_REVDATA)
save_start_layout.addWidget(self.save_button, Qt.AlignmentFlag.AlignLeft)
layout.addLayout(save_start_layout)
page.setLayout(layout)
return page
def select_file(self):
"""OpenFile Dialog todo select File"""
file_path, _ = QFileDialog.getOpenFileName(self, "Select File", "", "Text Files (*.txt)")
if file_path:
self.ip_list = read_list(file_path)
if self.ip_list:
self.start_button.setEnabled(True)
else:
return
#print("The selected file is empty or cannot be read.")
def START_Rev(self):
"""Function when users click start Reverse IP"""
if not self.ip_list:
#print("No IP addresses to process.")
return
self.console_area.clear()
self.result_data = ""
# Which users was selected
if self.api1_radio.isChecked():
selected_api = "API #1"
elif self.api2_radio.isChecked():
selected_api = "API #2"
else:
selected_api = "API #BOTH"
if not self.save_path:
return
self.tasker = DomainCheckerWorker(self.ip_list, self.save_path, selected_api)
self.tasker.progress.connect(self.update_console)
self.tasker.start()
def SAVE_REVDATA(self):
"""Save Result"""
save_path, _ = QFileDialog.getSaveFileName(self, "Save File", f"{current_dir}", "Text Files (*.txt)")
if save_path:
try:
self.save_path = save_path
with open(save_path, 'w') as file:
file.write(self.result_data)
except:
pass
def update_console(self, message):
self.console_area.appendPlainText(message)
def animate_page_transition(self, new_index):
self.pages.setCurrentIndex(new_index)
def toggle_sidebar(self, event):
titles = ["Reverse IP", "Generate IP", "Google", "Zone-H"]
"""https://youtu.be/Kp8lA294U1g
Toggle with animation on sidebar"""
if self.sidebar_expanded:
self.animate_sidebar(self.sidebar_width, self.collapsed_sidebar_width)
for button in self.page_buttons:
button.setText("")
button.setIconSize(self.icon_size)
button.setStyleSheet("""
QPushButton {
background-color: #34495E;
color: white;
font-size: 16px;
border: none;
text-align: center;
}
QPushButton:hover {
background-color: #1ABC9C;
}
QPushButton:pressed {
background-color: #16A085;
}
""")
else:
self.animate_sidebar(self.collapsed_sidebar_width, self.sidebar_width)
for i, button in enumerate(self.page_buttons):
button.setText(titles[i]) # Restore the text from titles list
button.setIconSize(self.icon_size)
button.setStyleSheet("""
QPushButton {
background-color: #34495E;
color: white;
font-size: 14px;
border: none;
padding-left: 10px;
text-align: left;
}
QPushButton:hover {
background-color: #1ABC9C;
}
QPushButton:pressed {
background-color: #16A085;
}
""")
self.sidebar_expanded = not self.sidebar_expanded
#Smooth animation with func toggle_sidebar
def animate_sidebar(self, start_width, end_width):
self.animation = QPropertyAnimation(self.sidebar, b"minimumWidth")
self.animation.setDuration(300) # Animation duration in milliseconds
self.animation.setStartValue(start_width)
self.animation.setEndValue(end_width)
self.animation.start()
#############################################################################
#Page 2
##https://www.digitalocean.com/community/tutorials/python-get-ip-address-from-hostname
class DomainToIP_Func(QThread):
result_signal = pyqtSignal(str)
def __init__(self, domain, path_result, parent=None):
super().__init__(parent)
self.domain = domain
self.save_file = path_result
def run(self):
try:
#maybe skip result if reduce number with this will make your code slow but make its work without skip IP
socket.setdefaulttimeout(7)
ip_address = socket.gethostbyname(self.domain)
self.result_signal.emit(f"{self.domain} --> {ip_address}")
with open(self.save_file, 'a') as data_ip:
data_ip.write(ip_address + '\n')
except socket.error:
self.result_signal.emit(f"{self.domain} --> Failed")
class DomainToIPPage(QWidget):
def __init__(self):
super().__init__()
self.ip_list = []
self.threads = []
self.save_path = ""
self.qt_UI()
def qt_UI(self):
layout = QVBoxLayout(self)
self.setLayout(layout)
# JUST ADD Title
title_label = QLabel("Bulk Domain to IP")
title_label.setFont(QFont("Arial", 18, QFont.Weight.Bold))
title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(title_label)
# Button Row for domain operations (small buttons)
button_layout = QHBoxLayout()
button_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
button_layout.setSpacing(20) # Add spacing between buttons
# Console Log for Reverse IP Lookup (rich text box)
reverse_ip_console_section = QGroupBox("")
reverse_ip_console_layout = QVBoxLayout()
self.console_log = QTextEdit(self)
self.console_log.setPlaceholderText("Console Log")
self.console_log.setReadOnly(True)
self.console_log.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.console_log.setStyleSheet(Editor_TXTStyle())
# self.console_log.setStyleSheet("""
# QTextEdit {
# background-color: #2C3E50;
# color: #ECF0F1;
# border: 2px solid #1ABC9C;
# padding: 10px;
# font-size: 14px;
# border-radius: 10px;
# }
# QScrollBar:vertical {
# border: none;
# background: rgb(52, 73, 94);
# width: 14px;
# margin: 15px 0 15px 0;
# border-radius: 7px; /* Rounded corners for the scrollbar background */
# }
# /* HANDLE BAR VERTICAL */
# QScrollBar::handle:vertical {
# background-color: rgb(26, 188, 156);
# min-height: 30px;
# border-radius: 7px; /* Rounded corners for the handle */
# }
# QScrollBar::handle:vertical:hover {
# background-color: #1ABC9C;
# }
# QScrollBar::handle:vertical:pressed {
# background-color: #16A085;
# }
# /* BTN TOP - SCROLLBAR */
# QScrollBar::sub-line:vertical {
# height: 0px;
# }
# /* BTN BOTTOM - SCROLLBAR */
# QScrollBar::add-line:vertical {
# height: 0px;
# }
# /* RESET ARROW */
# QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical {
# width: 0px;
# height: 0px;
# }
# QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
# background: none;
# }
# """)
reverse_ip_console_layout.addWidget(self.console_log)
reverse_ip_console_section.setLayout(reverse_ip_console_layout)
layout.addWidget(reverse_ip_console_section)
self.load_txt_btn = QPushButton('Domain Load')
self.load_txt_btn.setFixedSize(150, 40)
self.load_txt_btn.setStyleSheet(button_style())
self.load_txt_btn.clicked.connect(self.Select_List)
self.start_dom2ip_btn = QPushButton('Start Dom2IP')
self.start_dom2ip_btn.setFixedSize(150, 40)
self.start_dom2ip_btn.setStyleSheet(button_start())
self.start_dom2ip_btn.setEnabled(False)
self.start_dom2ip_btn.clicked.connect(self.START_DumperIP)
self.save_result_btn = QPushButton('Save IPList')
self.save_result_btn.setFixedSize(200, 40)
self.save_result_btn.setStyleSheet(button_style())
self.save_result_btn.clicked.connect(self.Save_ReverseIPResults)
button_layout.addWidget(self.load_txt_btn)
button_layout.addWidget(self.start_dom2ip_btn)
button_layout.addWidget(self.save_result_btn)
layout.addLayout(button_layout)
ip_output_console_section = QGroupBox("")
ip_output_console_layout = QVBoxLayout()
self.ip_console = QTextEdit(self)
self.ip_console.setPlaceholderText("Generated Log")
self.ip_console.setReadOnly(True)
self.ip_console.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.ip_console.setStyleSheet(Editor_TXTStyle())
ip_output_console_layout.addWidget(self.ip_console)
ip_output_console_section.setLayout(ip_output_console_layout)
layout.addWidget(ip_output_console_section)
input_layout = QHBoxLayout()
self.how_many_ip_input = QLineEdit(self)
self.how_many_ip_input.setFixedWidth(250)
self.how_many_ip_input.setPlaceholderText("How many IPs")
self.how_many_ip_input.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.how_many_ip_input.setStyleSheet(input_style())
self.generate_ip_btn = QPushButton("Generate IP")
self.generate_ip_btn.setFixedSize(150, 40)
self.generate_ip_btn.setStyleSheet(button_start())
self.generate_ip_btn.clicked.connect(self.Generator_IP)
self.save_generated_ip_btn = QPushButton("Save GenerateList")
self.save_generated_ip_btn.setFixedSize(150, 40)
self.save_generated_ip_btn.setStyleSheet(button_style())
self.save_generated_ip_btn.clicked.connect(self.Save_GeneratedIP)
input_layout.addWidget(self.generate_ip_btn)
input_layout.addWidget(self.how_many_ip_input)
input_layout.addWidget(self.save_generated_ip_btn)
layout.addLayout(input_layout)
def URL2HOST(self, site):
if site.startswith("http://"):
site = site.replace("http://", "")
elif site.startswith("https://"):
site = site.replace("https://", "")
site = site.strip()
pattern = re.compile(r'([^/]+)')
match = re.findall(pattern, site)
if match:
domain = match[0]
else:
domain = urlparse(site).netloc
return domain
def Select_List(self):
"""OpenFILE Select IP"""
iplist_path, _ = QFileDialog.getOpenFileName(self, "Select File", "", "Text Files (*.txt)")
if iplist_path:
with open(iplist_path, 'r') as file:
self.ip_list = [line.strip() for line in file]
if self.ip_list:
self.start_dom2ip_btn.setEnabled(True) # Enable Domain to IP Start_BTN
else:
print("The selected file is empty or cannot be read.")
def START_DumperIP(self):
if not self.ip_list:
self.console_log.setPlainText("No domains loaded.")
return
if not self.save_path:
QMessageBox.warning(self, "No Save Path", "Please select a valid save path.")
return
self.console_log.append("IPLog will show results:")
for domain in self.ip_list:
thread = DomainToIP_Func(domain, self.save_path)
thread.result_signal.connect(self.update_console_log)
thread.start()
self.threads.append(thread)
def update_console_log(self, result):
self.console_log.append(result)
def Generator_IP(self):
try:
count = int(self.how_many_ip_input.text())
generated_ips = []
for _ in range(count):
ip = f"{random.randint(1, 255)}.{random.randint(0, 255)}.{random.randint(0, 255)}.{random.randint(1, 255)}"
generated_ips.append(ip)
self.ip_console.setPlainText("\n".join(generated_ips))
except ValueError:
self.ip_console.setPlainText("Please Type number of IP you want Generate")
def Save_GeneratedIP(self):
save_ip, _ = QFileDialog.getSaveFileName(self, "Save File", f"{current_dir}", "Text Files (*.txt)")
if save_ip:
try:
with open(save_ip, 'w') as file:
file.write(self.ip_console.toPlainText())
self.ip_console.setPlainText(f"Generated IPs saved successfully to: {save_ip}")
except Exception as e:
self.ip_console.setPlainText(f"Error saving file: {e}")
def Save_ReverseIPResults(self):
self.save_path, _ = QFileDialog.getSaveFileName(self, "Save File", f"{current_dir}", "Text Files (*.txt)")
if self.save_path:
try:
with open(self.save_path, 'w') as file:
file.write(self.console_log.toPlainText())
self.console_log.setPlainText(f"Reverse IP results saved successfully to: {self.save_path}")
except Exception as e:
self.console_log.setPlainText(f"Error saving file: {e}")
#This coded was operated in 2022-2023 jst remastered
class KillAllPageWorker(QThread):
result_signal = pyqtSignal(str) # SENT RESULT
def __init__(self, query_list, n_pages, blocked_domains, save_path, domain_radio, url_radio, url_domain_radio, parent=None):
super().__init__(parent)
self.path_file = save_path
self.dork_queries = query_list
self.n_pages = n_pages
self.blocked_domains = blocked_domains
self.domain_radio = domain_radio
self.url_radio = url_radio
self.url_domain_radio = url_domain_radio
def Domain_Kicker(self, domain):
domain_data = str(domain).split('/')
domain_value = domain_data[0] + "//" + domain_data[2] + '/'
return domain_value
def captcha_google(self, driver):
"""Handles CAPTCHA detection and waits for it to resolve"""
try:
element = driver.find_element(By.XPATH, "//div[@class='g-recaptcha']")
WebDriverWait(driver, timeout=5000).until(EC.staleness_of(element)) # Wait until CAPTCHA is solved
except Exception as e:
print(f"Error handling CAPTCHA: {e}") # Log any errors encountered
def run(self):
driver_path = ChromeDriverManager().install()
options = Options()
options.add_argument("--log-level=3")
options.add_experimental_option("excludeSwitches", ["disable-logging"])
driver = webdriver.Chrome(service=Service(driver_path), options=options)
try:
for query_x in self.dork_queries:
for page in range(1, self.n_pages + 1):
url = f"http://www.google.com/search?q={query_x}&start={(page - 1) * 10}"
TRYNG = True
while TRYNG:
driver.get(url)
soup = BeautifulSoup(driver.page_source, 'html.parser')
# check if search got captcha
if 'Our systems have detected' in soup.text:
self.captcha_google(driver) #manually complete captcha and retry
else:
TRYNG = False #Try Again after complete captcha
search = soup.find_all('div', class_="yuRUbf")
if search:
for h in search:
link = h.a['href']
if link:
blocked = False
for blocked_domain in self.blocked_domains:
if blocked_domain in link:
blocked = True
break
if not blocked:
result = f"[{page}] : {link}"
self.result_signal.emit(result)
if self.domain_radio:
domain_ = self.Domain_Kicker(link)
with open(f'{self.path_file}_Domain.txt', 'a') as file:
file.write(domain_ + '\n')
if self.url_radio:
with open(f'{self.path_file}_URLink.txt', 'a') as file:
file.write(link + '\n')
if self.url_domain_radio:
domain_ = self.Domain_Kicker(link)
with open(f'{self.path_file}_URLink.txt', 'a') as file:
file.write(link + '\n')
with open(f'{self.path_file}_Domain.txt', 'a') as file:
file.write(domain_ + '\n')
except:
pass
class Google_Grabber(QWidget):
def __init__(self):
super().__init__()
self.save_path = ""
self.threads = []
self.qt_UI()
def qt_UI(self):
layout = QVBoxLayout(self)
self.setLayout(layout)
# Just title or you can remove it
mode_selection_label = QLabel("Wizard Grabber :D !!")
mode_selection_label.setFont(QFont("Arial", 20, QFont.Weight.Bold))
layout.addWidget(mode_selection_label)
radio_and_input_layout = QHBoxLayout()
radio_font = QFont("Arial", 12)
self.domain_radio = QRadioButton('Domain ')
self.domain_radio.setFont(radio_font)
self.url_radio = QRadioButton('URL ')
self.url_radio.setFont(radio_font)
self.url_domain_radio = QRadioButton('Domain + URL ')
self.url_domain_radio.setFont(radio_font)
radio_and_input_layout.addWidget(self.domain_radio)
radio_and_input_layout.addWidget(self.url_radio)
radio_and_input_layout.addWidget(self.url_domain_radio)
radio_and_input_layout.addStretch()
#num input of page you want scrape of google
self.number_input_label = QLabel("How Many Pages:")
self.number_input_label.setFont(QFont("Arial", 12))
self.Num_Page = QSpinBox()
self.Num_Page.setFixedSize(70, 40)
self.Num_Page.setFont(QFont("Arial", 12))
self.Num_Page.setRange(1, 100)
self.Num_Page.setValue(10)
self.Num_Page.setStyleSheet("""
QSpinBox::up-button, QSpinBox::down-button {
width: 0;
height: 0;
border: none;
}
QSpinBox {
padding-right: 5px; /* Adjust padding to make it look good without arrows */
}
""")
radio_and_input_layout.addWidget(self.number_input_label)
radio_and_input_layout.addWidget(self.Num_Page)
layout.addLayout(radio_and_input_layout)
# TextArea input Dork_List
self.dork_list = QTextEdit()
self.dork_list.setPlaceholderText("Dork_List here")
self.dork_list.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.dork_list.setStyleSheet(Editor_TXTStyle())
# TextArea add block_domain
self.block_domain = QTextEdit()
self.block_domain.setPlaceholderText("Filter Block Domain : Must add without http/https or www")
self.block_domain.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.block_domain.setStyleSheet(Editor_TXTStyle())
# Load Block_domains from file TXT
self.Load_DomainBlocker()
# Domain_LOG textArea
self.domain_result = QTextEdit()
self.domain_result.setPlaceholderText("Domain Logged")
self.domain_result.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.domain_result.setStyleSheet(Editor_TXTStyle())
# Dork List, Add Block Domain, and Domain Result
list_layout = QHBoxLayout()
left_layout = QVBoxLayout()
left_layout.addWidget(self.dork_list)
left_layout.addWidget(self.block_domain)
list_layout.addLayout(left_layout)
list_layout.addWidget(self.domain_result)
layout.addLayout(list_layout)
# Bottom Buttons (Start Grabber, Save Result, Save Blocked Domains)
button_layout = QHBoxLayout()
self.start_grabber_btn = QPushButton('Start Grabber')
self.start_grabber_btn.setFixedSize(150, 40)
self.start_grabber_btn.setStyleSheet(button_start())
self.start_grabber_btn.clicked.connect(self.start_grabber)
self.save_result_btn = QPushButton('Save Result')
self.save_result_btn.setFixedSize(150, 40)
self.save_result_btn.setStyleSheet(button_style())
self.save_blocked_domains_btn = QPushButton('Save Blocked Domains')
self.save_blocked_domains_btn.setFixedSize(200, 40)
self.save_blocked_domains_btn.setStyleSheet(button_style())
self.save_result_btn.clicked.connect(self.SaveResult)
self.save_blocked_domains_btn.clicked.connect(self.save_blocked_domains)
button_layout.addWidget(self.save_blocked_domains_btn)
button_layout.addWidget(self.start_grabber_btn)
button_layout.addWidget(self.save_result_btn)
layout.addLayout(button_layout)
def save_blocked_domains(self):
blocked_domains = self.block_domain.toPlainText()
with open('domain_block.txt', 'w') as file:
file.write(blocked_domains)
def SaveResult(self):
self.save_path, _ = QFileDialog.getSaveFileName(self, "Save File", f"{current_dir}", "Text Files (*.txt)")
if not self.save_path:
QMessageBox.warning(self, "No Save Path", "Please select a valid save path.")
return False
if not (self.domain_radio.isChecked() or self.url_radio.isChecked() or self.url_domain_radio.isChecked()):
QMessageBox.warning(self, "No Option Selected", "Please select Domain, URL, or Domain + URL option.")
return False
self.path_file, file_extension = os.path.splitext(self.save_path)
try:
if self.domain_radio.isChecked():
domain_file_path = f'{self.path_file}_Domain.txt'
with open(domain_file_path, 'w') as domain_file:
domain_file.write(self.domain_result.toPlainText())
elif self.url_radio.isChecked():
url_file_path = f'{self.path_file}_URLink.txt'
with open(url_file_path, 'w') as url_file:
url_file.write(self.domain_result.toPlainText())
elif self.url_domain_radio.isChecked():
domain_file_path = f'{self.path_file}_Domain.txt'
with open(domain_file_path, 'w') as domain_file:
domain_file.write(self.domain_result.toPlainText())
url_file_path = f'{self.path_file}_URLink.txt'
with open(url_file_path, 'w') as url_file:
url_file.write(self.domain_result.toPlainText())
QMessageBox.information(self, "Save Successful", "Files saved successfully!")
return True
except Exception as e:
QMessageBox.critical(self, "Error Saving", f"Error Saving : {e}")
return False
def start_grabber(self):
self.path_file, file_extension = os.path.splitext(self.save_path)
if not self.path_file:
QMessageBox.information(self, "Not Found", "Please Save Result *txt")
return
#dork_input
dork_queries = self.dork_list.toPlainText().splitlines()
n_pages = self.Num_Page.value()
blocked_domains = self.Load_DomainBlocker()
# Check if a domain filter is selected
domain_radio_checked = self.domain_radio.isChecked()
url_radio_checked = self.url_radio.isChecked()
url_domain_radio_checked = self.url_domain_radio.isChecked()
if not (domain_radio_checked or url_radio_checked or url_domain_radio_checked):
QMessageBox.warning(self, "No Option Selected", "Please select Domain, URL, or Domain + URL option.")
return
self.task_th = KillAllPageWorker(dork_queries, n_pages, blocked_domains, self.path_file,
domain_radio_checked, url_radio_checked, url_domain_radio_checked)
self.task_th.result_signal.connect(self.update_result)
self.task_th.start()
def update_result(self, result):
self.domain_result.append(result)
def Load_DomainBlocker(self):
blocked_domains_list = []
if os.path.exists('domain_block.txt'):
with open('domain_block.txt', 'r') as file:
blocked_domains = file.readlines()
blocked_domains_list = [domain.strip() for domain in blocked_domains] # Strip spaces/newlines
self.block_domain.setPlainText("\n".join(blocked_domains_list)) # Set Display in the txtArea
else:
self.block_domain.setPlainText("") # Clear if no file domain_block.txt
return blocked_domains_list
class ZoneH_GrabThread(QThread):
update_output = pyqtSignal(str)
def __init__(self, attacker_name, save_path, n_pages):
super().__init__()
self.attacker_name = attacker_name
self.n_pages = n_pages
self.save_located = save_path
def setup_driver(self):
chrome_options = Options()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])
chrome_options.add_argument('--log-level=0')
# chrome_options.add_argument('--headless') #dont run with headless will be hidden chrome and users cant see captcha to complete in manually
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)
return driver
def captcha(self, driver):
element = driver.find_element('xpath', "//*[text() = 'Copy the code:']")
datax = WebDriverWait(driver, timeout=2000).until(EC.staleness_of(element))
return datax
def run(self):
driver = self.setup_driver()
i = 0
try:
for page in range(self.n_pages):
url = f"https://www.zone-h.org/archive/notifier={self.attacker_name}/page={page}"
driver.get(url)
soup = BeautifulSoup(driver.page_source, 'html.parser')
if 'Copy the code:' in soup.text and 'If you often get this captcha when gathering data, please contact us' in soup.text:
self.update_output.emit("[INFO] Please solve Captcha in manually.")
self.captcha(driver)
else:
if 'Total notifications' in soup.text and 'Legend' in soup.text:
try:
tablezoneh = soup.find('table', {'id': 'ldeface'})
bodyzoneh = tablezoneh.find('tbody')
for tr in bodyzoneh.find_all('tr'):
tds = tr.find_all('td')
if len(tds) < 8:
continue
date = tds[0].text.strip()
domain = tds[7].text.strip()
if 'Domain' not in domain:
i += 1
if '/' in domain:
domain = str(domain.split('/', 1)[0])
if '...' in domain:
domain = str(domain).replace('...', '')
output = f'[{i}] Domain : {domain} ---> Page >>> {page} | {date}'
self.update_output.emit(output)
with open(f'{self.save_located}', 'a') as f_takedown:
f_takedown.write(domain + '\n')
with open('Zeus_Result/AllZoneH_Domain.txt', 'a') as f_all:
f_all.write(domain + '\n')
except Exception as e:
self.update_output.emit(f"Error Page {page}: {e}")
driver.quit()
except Exception as e:
self.update_output.emit(f"Error: {e}")
#Func has coded in 2022-23
#Warning page 51 no need change all just 51 pages only no more less
class ZoneH_Grabber(QWidget):
def __init__(self):
super().__init__()
self.save_path = ""
self.init_UI()
def init_UI(self):
self.setWindowTitle("ZoneH Grabber")
self.setGeometry(100, 100, 600, 400)
layout = QVBoxLayout()
attacker_label = QLabel("Attacker Name:")
attacker_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #ECF0F1;")
layout.addWidget(attacker_label)
self.attacker_input = QLineEdit(self)
self.attacker_input.setPlaceholderText("Enter Attacker Name")
self.attacker_input.setStyleSheet(input_style())
layout.addWidget(self.attacker_input)
self.output_box = QTextEdit(self)
self.output_box.setReadOnly(True)
self.output_box.setPlaceholderText("Zone-Log")
self.output_box.setStyleSheet(Editor_TXTStyle())
layout.addWidget(self.output_box)
button_layout = QHBoxLayout()
self.save_button = QPushButton("Save", self)
self.save_button.setFixedSize(150, 40)
self.save_button.setStyleSheet(button_style())
self.save_button.clicked.connect(self.Save_DATA)
self.grab_button = QPushButton("START-H", self)
self.grab_button.setFixedSize(150, 40)
self.grab_button.setStyleSheet("""
QPushButton {
background-color: #8A2BE2;
color: white;
font-size: 16px;
border: none;
}
QPushButton:hover {
background-color: #1ABC9C;
}
QPushButton:pressed {
background-color: cyan;
}
""")
self.grab_button.clicked.connect(self.START_ZH)
button_layout.addWidget(self.grab_button)
button_layout.addWidget(self.save_button)
layout.addLayout(button_layout)
self.setLayout(layout)
def Save_DATA(self):
attacker_name = self.attacker_input.text()
if attacker_name:
# OpenDialog Start Zeus_Result folder
self.save_path, _ = QFileDialog.getSaveFileName(self, "Save File", f"{current_dir}/{attacker_name}", "Text Files (*.txt)")
if self.save_path:
try:
with open(self.save_path, 'w') as file:
file.write(self.output_box.toPlainText())
# except Exception as e:
# print(f"Error saving file: {e}")
except:
pass
def START_ZH(self):
attacker_name = self.attacker_input.text()
if not self.save_path:
QMessageBox.warning(self, "No Save Path", "Please select a valid save path.")
else:
#verify again
if attacker_name or self.save_path:
self.grab_thread = ZoneH_GrabThread(attacker_name, self.save_path, 51)
self.grab_thread.update_output.connect(self.update_console)
self.grab_thread.start()
def update_console(self, text):
self.output_box.append(text)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())