1346 lines
50 KiB
Python
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())
|