#!/usr/bin/env python3
# main.py — iOS Activation Bypass GUI (macOS, PyQt6)
import sys
import os
import re
import time
from pathlib import Path
from typing import Optional, Dict, Any
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QTextEdit, QLabel, QLineEdit, QGroupBox,
QRadioButton, QButtonGroup, QMessageBox, QProgressBar, QFrame
)
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QObject, QTimer, QSettings
from PyQt6.QtGui import QFont, QTextCursor, QPalette, QColor, QPixmap, QIcon
# === CLI module import ===
try:
from activator_macos import log as original_log, run as original_run, validate_guid, run_cmd, find_binary
except ImportError as e:
app = QApplication(sys.argv) # temporary QApplication for dialog
QMessageBox.critical(None, "Import Error", f"Failed to import 'activator_macos.py':\n{e}\n\n"
"Ensure it's in the same directory.")
sys.exit(1)
# === Thread-safe signal communication ===
class SignalEmitter(QObject):
log_signal = pyqtSignal(str, str)
progress_signal = pyqtSignal(int)
success_signal = pyqtSignal()
error_signal = pyqtSignal(str)
device_update_signal = pyqtSignal(dict)
stage_signal = pyqtSignal(str)
emitter = SignalEmitter()
def gui_log(msg: str, level='info'):
emitter.log_signal.emit(msg, level)
original_log(msg, level)
# Patch the activator logger
import activator_macos
activator_macos.log = gui_log
# === Worker thread ===
class ActivatorWorker(QThread):
def __init__(self, auto: bool = False, guid: Optional[str] = None):
super().__init__()
self.auto = auto
self.guid = guid
self._stopped = False
def stop(self):
self._stopped = True
self.requestInterruption()
def _set_stage(self, stage_name: str):
emitter.stage_signal.emit(stage_name)
def run(self):
try:
if self._stopped:
return
# Progress through stage signals (approximate)
QTimer.singleShot(300, lambda: self._set_stage("detect"))
QTimer.singleShot(1000, lambda: self._set_stage("guid"))
QTimer.singleShot(4000, lambda: self._set_stage("download"))
QTimer.singleShot(9000, lambda: self._set_stage("upload"))
QTimer.singleShot(16000, lambda: self._set_stage("reboot"))
original_run(auto=self.auto, preset_guid=self.guid)
if not self._stopped:
self._set_stage("done")
emitter.success_signal.emit()
except Exception as e:
if not self._stopped:
emitter.error_signal.emit(str(e))
finally:
if not self._stopped:
emitter.progress_signal.emit(100)
# === Device information panel ===
class DeviceInfoPanel(QWidget):
def __init__(self):
super().__init__()
self.setup_ui()
self.update_info()
def setup_ui(self):
layout = QHBoxLayout()
layout.setContentsMargins(12, 12, 12, 12)
layout.setSpacing(16)
# Left side: status + image
img_container = QVBoxLayout()
img_container.setAlignment(Qt.AlignmentFlag.AlignTop)
self.status_indicator = QLabel("●")
self.status_indicator.setFont(QFont("SF Mono", 16))
self.status_indicator.setStyleSheet("color: #999;")
img_container.addWidget(self.status_indicator, alignment=Qt.AlignmentFlag.AlignLeft)
self.img_label = QLabel()
img_path = Path("assets/iphone.png")
if img_path.exists():
pixmap = QPixmap(str(img_path))
size = 128
screen = QApplication.primaryScreen()
if screen:
size = int(128 * screen.devicePixelRatio())
pixmap = pixmap.scaled(size, size, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
self.img_label.setPixmap(pixmap)
else:
self.img_label.setText("📱\n\niPhone\n(not found)")
self.img_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.img_label.setStyleSheet("font-size: 14px; color: #888;")
img_container.addWidget(self.img_label)
layout.addLayout(img_container)
# Right side: text
info_layout = QVBoxLayout()
info_layout.setSpacing(6)
title_label = QLabel("Device Information")
title_label.setFont(QFont("Segoe UI", 11, QFont.Weight.Bold))
info_layout.addWidget(title_label)
self.model_label = QLabel("Model: —")
self.ios_label = QLabel("iOS: —")
self.activation_label = QLabel("Activation: —")
self.udid_label = QLabel("UDID: —")
for lbl in [self.model_label, self.ios_label, self.activation_label, self.udid_label]:
lbl.setFont(QFont("SF Mono, Menlo, monospace", 10))
info_layout.addWidget(lbl)
layout.addLayout(info_layout)
self.setLayout(layout)
def update_info(self, info: Optional[Dict[str, str]] = None):
if not info or not isinstance(info, dict):
info = {"ProductType": "—", "ProductVersion": "—", "ActivationState": "—", "UniqueDeviceID": "—"}
# Status
act = info.get("ActivationState", "").strip()
if act == "Activated":
self.status_indicator.setStyleSheet("color: #4caf50;")
self.status_indicator.setToolTip("✅ Activated")
elif act in ("Unactivated", "—"):
self.status_indicator.setStyleSheet("color: #ff9800;")
self.status_indicator.setToolTip("⚠ Not activated")
else:
self.status_indicator.setStyleSheet("color: #f44336;")
self.status_indicator.setToolTip("❌ Unknown")
# Model
model_map = {
"iPhone13,4": "iPhone 12 Pro Max",
"iPhone13,3": "iPhone 12 Pro",
"iPhone13,2": "iPhone 12",
"iPhone14,5": "iPhone 13",
"iPhone15,2": "iPhone 14 Pro",
"iPhone15,3": "iPhone 14 Pro Max",
"iPhone16,1": "iPhone 15 Pro",
"iPhone16,2": "iPhone 15 Pro Max",
}
code = info.get("ProductType", "Unknown")
name = model_map.get(code, code)
self.model_label.setText(f"Model: {name}")
# Other info
self.ios_label.setText(f"iOS: {info.get('ProductVersion', '—')}")
state = info.get("ActivationState", "—")
color = "#4caf50" if state == "Activated" else "#ff9800"
self.activation_label.setText(f'Activation: {state}')
self.activation_label.setTextFormat(Qt.TextFormat.RichText)
udid = info.get("UniqueDeviceID", "—")
if len(udid) > 10:
udid = udid[:6] + "…" + udid[-4:]
self.udid_label.setText(f"UDID: {udid}")
# === Main window ===
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("📱 iOS Activation Bypass (GUI)")
self.resize(920, 700)
self.thread = None
# ✅ QSettings — only after QApplication
self.settings = QSettings("Rust505", "iOSActivatorGUI")
# Device update timer
self._device_timer = QTimer()
self._device_timer.timeout.connect(self.detect_device)
self._device_timer.start(3000)
# Central widget
central = QWidget()
self.setCentralWidget(central)
main_layout = QVBoxLayout(central)
main_layout.setContentsMargins(16, 16, 16, 16)
main_layout.setSpacing(12)
# Device panel
device_frame = QFrame()
device_frame.setFrameShape(QFrame.Shape.StyledPanel)
device_frame.setFrameShadow(QFrame.Shadow.Raised)
device_layout = QVBoxLayout(device_frame)
device_layout.addWidget(QLabel("📱 Connected Device"), alignment=Qt.AlignmentFlag.AlignLeft)
self.device_panel = DeviceInfoPanel()
device_layout.addWidget(self.device_panel)
main_layout.addWidget(device_frame)
# GUID mode
mode_group = QGroupBox("GUID Input Mode")
mode_layout = QHBoxLayout()
self.radio_auto = QRadioButton("Auto-detect (recommended)")
self.radio_manual = QRadioButton("Manual input")
self.radio_auto.setChecked(True)
group = QButtonGroup()
group.addButton(self.radio_auto)
group.addButton(self.radio_manual)
mode_layout.addWidget(self.radio_auto)
mode_layout.addWidget(self.radio_manual)
mode_group.setLayout(mode_layout)
main_layout.addWidget(mode_group)
# GUID field
guid_layout = QHBoxLayout()
guid_layout.addWidget(QLabel("GUID:"))
self.guid_edit = QLineEdit()
self.guid_edit.setPlaceholderText("e.g. 1A2B3C4D-1234-4123-8888-ABCDEF012345")
self.guid_edit.textChanged.connect(self._validate_guid)
self.guid_edit.setMaximumWidth(500)
guid_layout.addWidget(self.guid_edit)
main_layout.addLayout(guid_layout)
# Buttons
btn_layout = QHBoxLayout()
self.start_btn = QPushButton("▶ Start Activation")
self.stop_btn = QPushButton("⏹ Stop")
self.stop_btn.setEnabled(False)
self.start_btn.clicked.connect(self.start_activation)
self.stop_btn.clicked.connect(self.stop_activation)
btn_layout.addWidget(self.start_btn)
btn_layout.addWidget(self.stop_btn)
main_layout.addLayout(btn_layout)
# Progress
self.progress = QProgressBar()
self.progress.setRange(0, 100)
self.progress.setValue(0)
main_layout.addWidget(self.progress)
# Logs
log_group = QGroupBox("Logs")
log_layout = QVBoxLayout()
self.log_view = QTextEdit()
self.log_view.setReadOnly(True)
self.log_view.setFont(QFont("SF Mono, Menlo, Monaco, monospace", 10))
self.log_view.setStyleSheet("background: #1e1e1e; color: #e0e0e0;")
log_layout.addWidget(self.log_view)
log_group.setLayout(log_layout)
main_layout.addWidget(log_group)
# Connect signals
emitter.log_signal.connect(self.append_log)
emitter.progress_signal.connect(self.progress.setValue)
emitter.success_signal.connect(self.on_success)
emitter.error_signal.connect(self.on_error)
emitter.device_update_signal.connect(self.device_panel.update_info)
emitter.stage_signal.connect(self._on_stage_change)
# Load last GUID (if exists)
last_guid = self.settings.value("last_guid", "")
if last_guid and validate_guid(last_guid):
self.radio_manual.setChecked(True)
self.guid_edit.setText(last_guid.upper())
# Check dependencies and update device
self._check_dependencies()
self.detect_device()
def _check_dependencies(self):
missing = [b for b in ['ideviceinfo', 'idevice_id', 'pymobiledevice3', 'curl'] if not find_binary(b)]
if missing:
msg = (
f"⚠ Missing tools: {', '.join(missing)}\n\n"
"Install with:\n"
" brew install libimobiledevice usbmuxd\n"
" pip3 install -U pymobiledevice3"
)
QMessageBox.critical(self, "Dependency Error", msg)
self.start_btn.setEnabled(False)
def _validate_guid(self):
text = self.guid_edit.text().strip()
valid = bool(text and validate_guid(text.upper()))
self.guid_edit.setStyleSheet("" if valid else "background: #ffebee;" if len(text) > 8 else "")
def detect_device(self):
if self.thread and self.thread.isRunning():
return
try:
code, out, _ = run_cmd(["ideviceinfo"], timeout=5)
info = {}
if code == 0:
for line in out.splitlines():
if ": " in line:
k, v = line.split(": ", 1)
info[k.strip()] = v.strip()
# UDID
udid_code, udid_out, _ = run_cmd(["idevice_id", "-l"], timeout=3)
if udid_code == 0:
info["UniqueDeviceID"] = udid_out.strip() or "—"
emitter.device_update_signal.emit(info)
except Exception as e:
emitter.device_update_signal.emit({})
def _on_stage_change(self, stage: str):
labels = {
"detect": "🔍 Detecting device...",
"guid": "📡 Extracting GUID...",
"download": "📥 Downloading payload...",
"upload": "📤 Uploading to /Downloads/...",
"reboot": "🔄 Rebooting (×3)...",
"done": "✅ Done!",
}
self.progress.setFormat(labels.get(stage, stage))
def start_activation(self):
if self.thread and self.thread.isRunning():
return
auto = self.radio_auto.isChecked()
guid = self.guid_edit.text().strip().upper() if self.radio_manual.isChecked() else None
if self.radio_manual.isChecked():
if not guid:
QMessageBox.warning(self, "Input Required", "GUID field is empty.")
return
if not validate_guid(guid):
QMessageBox.warning(self, "Invalid GUID", "GUID must be UUID v4 (e.g. XXXXXXXX-XXXX-4XXX-YXXX-XXXXXXXXXXXX).")
return
self.log_view.clear()
self.append_log("🚀 Starting activation process...", "info")
self.start_btn.setEnabled(False)
self.stop_btn.setEnabled(True)
self.progress.setValue(0)
self.progress.setFormat("Initializing...")
self.thread = ActivatorWorker(auto=auto, guid=guid)
self.thread.finished.connect(self._on_thread_finished)
self.thread.start()
def stop_activation(self):
if self.thread and self.thread.isRunning():
self.append_log("⏹ Stop requested...", "warn")
self.thread.stop()
self.thread.wait(2000)
if self.thread.isRunning():
self.thread.terminate()
self.append_log("⚠ Thread forcibly terminated.", "error")
self._on_thread_finished()
def _on_thread_finished(self):
self.start_btn.setEnabled(True)
self.stop_btn.setEnabled(False)
def append_log(self, msg: str, level='info'):
colors = {'success': '#4caf50', 'error': '#f44336', 'warn': '#ff9800',
'step': '#2196f3', 'info': '#64b5f6', 'detail': '#90a4ae'}
color = colors.get(level, '#e0e0e0')
ts = time.strftime("%H:%M:%S")
html = f'[{ts}] {msg}
'
self.log_view.append(html)
self.log_view.moveCursor(QTextCursor.MoveOperation.End)
self.log_view.ensureCursorVisible()
def on_success(self):
self.append_log("🎉 ACTIVATION SUCCESSFUL!", "success")
# Save GUID
guid = self.guid_edit.text().strip().upper()
if guid and validate_guid(guid):
self.settings.setValue("last_guid", guid)
QMessageBox.information(self, "Success", "✅ Activation bypass completed!\n\n"
"Check Settings → General → About.")
self.detect_device()
def on_error(self, err: str):
self.append_log(f"❌ Fatal: {err}", "error")
QMessageBox.critical(self, "Activation Failed", f"Process terminated with error:\n\n{err}")
# === Theme and icon ===
def enable_dark_mode(app: QApplication):
try:
import subprocess
res = subprocess.run(["defaults", "read", "-g", "AppleInterfaceStyle"],
capture_output=True, text=True)
is_dark = res.returncode == 0 and "Dark" in res.stdout
except:
is_dark = True
if is_dark:
app.setStyle("Fusion")
p = QPalette()
p.setColor(QPalette.ColorRole.Window, QColor(38, 38, 38))
p.setColor(QPalette.ColorRole.WindowText, Qt.GlobalColor.white)
p.setColor(QPalette.ColorRole.Base, QColor(28, 28, 28))
p.setColor(QPalette.ColorRole.AlternateBase, QColor(48, 48, 48))
p.setColor(QPalette.ColorRole.ToolTipBase, Qt.GlobalColor.white)
p.setColor(QPalette.ColorRole.ToolTipText, Qt.GlobalColor.white)
p.setColor(QPalette.ColorRole.Text, Qt.GlobalColor.white)
p.setColor(QPalette.ColorRole.Button, QColor(68, 68, 68))
p.setColor(QPalette.ColorRole.ButtonText, Qt.GlobalColor.white)
p.setColor(QPalette.ColorRole.BrightText, Qt.GlobalColor.red)
p.setColor(QPalette.ColorRole.Link, QColor(40, 140, 240))
p.setColor(QPalette.ColorRole.Highlight, QColor(40, 140, 240))
p.setColor(QPalette.ColorRole.HighlightedText, Qt.GlobalColor.black)
app.setPalette(p)
def set_app_icon(app: QApplication):
icon_path = Path("assets/app_icon.png")
if icon_path.exists():
app.setWindowIcon(QIcon(str(icon_path)))
# === Entry point ===
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setApplicationName("iOS Activation Bypass")
app.setOrganizationName("Rust505")
enable_dark_mode(app)
set_app_icon(app)
window = MainWindow()
window.show()
sys.exit(app.exec())