main.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. import sys
  2. import os
  3. import platform
  4. import time
  5. import shutil
  6. import traceback
  7. from pathlib import Path
  8. from PyQt6.QtWidgets import (
  9. QApplication, QMainWindow, QLabel, QPushButton, QProgressBar,
  10. QVBoxLayout, QHBoxLayout, QWidget, QMessageBox, QFrame, QTextEdit
  11. )
  12. from PyQt6.QtGui import QPixmap, QFont, QLinearGradient, QPainter, QColor
  13. from PyQt6.QtCore import Qt, QThread, pyqtSignal, QTimer
  14. def setup_app_environment():
  15. """Configure environment for .app bundle"""
  16. try:
  17. if getattr(sys, 'frozen', False):
  18. bundle_path = Path(sys.executable).parent.parent.parent
  19. resources_path = bundle_path / 'Contents' / 'Resources'
  20. if str(resources_path) not in sys.path:
  21. sys.path.insert(0, str(resources_path))
  22. bin_dir = resources_path / 'bin'
  23. if bin_dir.exists():
  24. new_path = str(bin_dir) + ':' + os.environ.get('PATH', '')
  25. os.environ['PATH'] = new_path
  26. print(f"🔧 Added to PATH: {bin_dir}")
  27. os.chdir(resources_path)
  28. print(f"🔧 Working directory: {os.getcwd()}")
  29. return True
  30. except Exception as e:
  31. print(f"⚠️ Environment setup failed: {e}")
  32. return False
  33. def setup_logging():
  34. """Setup debug logging"""
  35. try:
  36. log_file = Path.home() / "Desktop" / "codex_app.log"
  37. with open(log_file, 'w') as f:
  38. f.write(f"=== Codex A12+ Log ===\n")
  39. f.write(f"Time: {time.ctime()}\n")
  40. f.write(f"Python: {sys.version}\n")
  41. f.write(f"Working dir: {os.getcwd()}\n")
  42. f.write(f"Frozen: {getattr(sys, 'frozen', False)}\n")
  43. f.write(f"PATH: {os.environ.get('PATH', '')}\n")
  44. except Exception as e:
  45. print(f"⚠️ Logging setup failed: {e}")
  46. # Setup environment before imports
  47. setup_app_environment()
  48. setup_logging()
  49. # Import core logic
  50. try:
  51. from activator import BypassAutomation
  52. print("✅ activator imported successfully")
  53. except Exception as e:
  54. print(f"❌ Failed to import activator: {e}")
  55. traceback.print_exc()
  56. try:
  57. app = QApplication(sys.argv)
  58. QMessageBox.critical(
  59. None,
  60. "❌ Import Error",
  61. f"Failed to import activator.py:\n{str(e)}\n\n"
  62. f"Current dir: {os.getcwd()}\n"
  63. f"Python path: {sys.path}"
  64. )
  65. sys.exit(1)
  66. except:
  67. sys.exit(1)
  68. class WorkerThread(QThread):
  69. log = pyqtSignal(str)
  70. finished = pyqtSignal(bool, str)
  71. device_info = pyqtSignal(str, str, str)
  72. progress = pyqtSignal(int)
  73. def run(self):
  74. try:
  75. self.log.emit("🔍 Verifying system requirements...")
  76. automation = BypassAutomation(auto_confirm_guid=True)
  77. automation.verify_dependencies()
  78. self.log.emit("📱 Detecting device...")
  79. automation.detect_device()
  80. udid = automation.device_info.get('UniqueDeviceID', 'Unknown')
  81. ios = automation.device_info.get('ProductVersion', 'Unknown')
  82. product = automation.device_info.get('ProductType', 'Unknown')
  83. self.device_info.emit(udid, ios, product)
  84. self.log.emit("🔑 Auto-detecting SystemGroup GUID (up to 5 attempts)...")
  85. guid = automation.get_guid_auto()
  86. if not guid:
  87. self.log.emit("⚠ GUID detection failed — using fallback GUID")
  88. guid = "00000000-0000-0000-0000-000000000000"
  89. automation.guid = guid
  90. self.log.emit(f"✅ Using GUID: {guid}")
  91. self.log.emit("🌐 Requesting payload URLs from server...")
  92. prd = automation.device_info['ProductType']
  93. sn = automation.device_info['SerialNumber']
  94. stage1_url, stage2_url, stage3_url = automation.get_all_urls_from_server(prd, guid, sn)
  95. if not all([stage1_url, stage2_url, stage3_url]):
  96. raise Exception("Server did not return all required URLs")
  97. self.progress.emit(10)
  98. self.log.emit("📥 Pre-loading Stage 1...")
  99. automation.preload_stage("stage1", stage1_url)
  100. self.progress.emit(20)
  101. self.log.emit("📥 Pre-loading Stage 2...")
  102. automation.preload_stage("stage2", stage2_url)
  103. self.progress.emit(30)
  104. self.log.emit("💾 Downloading final payload (Stage 3)...")
  105. local_db = "downloads.28.sqlitedb"
  106. if os.path.exists(local_db):
  107. os.remove(local_db)
  108. if not automation._curl_download(stage3_url, local_db):
  109. raise Exception("Final payload download failed")
  110. self.progress.emit(40)
  111. self.log.emit("🔍 Validating payload database...")
  112. import sqlite3
  113. conn = sqlite3.connect(local_db)
  114. try:
  115. cur = conn.cursor()
  116. cur.execute("SELECT COUNT(*) FROM asset")
  117. count = cur.fetchone()[0]
  118. if count == 0:
  119. raise Exception("Empty asset table — invalid payload")
  120. self.log.emit(f"✅ Database valid: {count} assets found")
  121. finally:
  122. conn.close()
  123. self.progress.emit(50)
  124. self.log.emit("📤 Uploading payload to /Downloads/ via AFC...")
  125. target = "/Downloads/downloads.28.sqlitedb"
  126. if automation.afc_mode == "ifuse":
  127. automation.mount_afc()
  128. fpath = automation.mount_point + target
  129. os.makedirs(os.path.dirname(fpath), exist_ok=True)
  130. if os.path.exists(fpath):
  131. os.remove(fpath)
  132. shutil.copy(local_db, fpath)
  133. self.log.emit("✅ Uploaded via ifuse")
  134. else:
  135. automation._run_cmd(["pymobiledevice3", "afc", "rm", target])
  136. code, _, err = automation._run_cmd(["pymobiledevice3", "afc", "push", local_db, target])
  137. if code != 0:
  138. raise Exception(f"AFC upload failed: {err}")
  139. self.log.emit("✅ Uploaded via pymobiledevice3")
  140. self.progress.emit(65)
  141. # Cleanup WAL/SHM
  142. for wal_file in ["/Downloads/downloads.28.sqlitedb-wal", "/Downloads/downloads.28.sqlitedb-shm"]:
  143. if automation.afc_mode == "ifuse":
  144. fpath = automation.mount_point + wal_file
  145. if os.path.exists(fpath):
  146. try:
  147. os.remove(fpath)
  148. except:
  149. pass
  150. else:
  151. automation._run_cmd(["pymobiledevice3", "afc", "rm", wal_file])
  152. self.log.emit("🧹 Cleaned up WAL/SHM files")
  153. # === STAGE 1: FIRST REBOOT + COPY TO /Books/ ===
  154. self.log.emit("🔄 Stage 1: Rebooting device...")
  155. if not automation.reboot_device():
  156. self.log.emit("⚠ First reboot failed — proceeding anyway")
  157. self.log.emit("⏳ Waiting for iTunesMetadata.plist (max 25s)...")
  158. if not automation.wait_for_file("/iTunes_Control/iTunes/iTunesMetadata.plist", timeout=25):
  159. raise Exception("iTunesMetadata.plist not found after reboot")
  160. self.log.emit("➡ Copying plist to /Books/iTunesMetadata.plist...")
  161. if not automation.afc_copy(
  162. "/iTunes_Control/iTunes/iTunesMetadata.plist",
  163. "/Books/iTunesMetadata.plist"
  164. ):
  165. raise Exception("Failed to copy plist to /Books/")
  166. self.log.emit("✅ Copied to /Books/")
  167. self.progress.emit(75)
  168. # === STAGE 2: SECOND REBOOT + COPY BACK ===
  169. self.log.emit("🔄 Stage 2: Rebooting again to trigger bookassetd...")
  170. if not automation.reboot_device():
  171. self.log.emit("⚠ Second reboot failed — proceeding anyway")
  172. self.log.emit("⏳ Waiting 15s for bookassetd processing...")
  173. time.sleep(15)
  174. self.log.emit("⬅ Copying plist back to /iTunes_Control/...")
  175. if not automation.afc_copy(
  176. "/Books/iTunesMetadata.plist",
  177. "/iTunes_Control/iTunes/iTunesMetadata.plist"
  178. ):
  179. self.log.emit("⚠ Warning: copy-back failed — activation may still work")
  180. self.log.emit("⏸ Waiting 20s for MobileActivation sync...")
  181. time.sleep(20)
  182. self.progress.emit(90)
  183. # === FINAL REBOOT ===
  184. self.log.emit("🔄 Final reboot to commit activation state...")
  185. if automation.reboot_device():
  186. self.log.emit("✅ Device rebooted — activation should complete shortly")
  187. else:
  188. self.log.emit("⚠ Final reboot failed — activation may still complete in background")
  189. self.progress.emit(100)
  190. self.finished.emit(True, "🎉 Activation process completed successfully!\n"
  191. "Device should activate within 1–2 minutes.")
  192. except Exception as e:
  193. error_msg = f"❌ {str(e)}"
  194. print(f"💥 Worker error: {error_msg}")
  195. traceback.print_exc()
  196. self.log.emit(error_msg)
  197. self.finished.emit(False, error_msg)
  198. class MainWindow(QMainWindow):
  199. def __init__(self):
  200. super().__init__()
  201. self.setWindowTitle("Codex A12+ — Activation Tool")
  202. self.setFixedSize(820, 560)
  203. central = QWidget()
  204. self.setCentralWidget(central)
  205. layout = QHBoxLayout(central)
  206. layout.setContentsMargins(0, 0, 0, 0)
  207. layout.setSpacing(0)
  208. # === iPhone image panel ===
  209. left = QLabel()
  210. left.setFixedSize(320, 560)
  211. left.setAlignment(Qt.AlignmentFlag.AlignCenter)
  212. # Try multiple paths for image
  213. image_paths = [
  214. "assets/iphone.png",
  215. "iphone.png",
  216. "../Resources/assets/iphone.png",
  217. str(Path.home() / "Desktop" / "iphone.png")
  218. ]
  219. pixmap = None
  220. for path in image_paths:
  221. if os.path.exists(path):
  222. p = QPixmap(path)
  223. if not p.isNull():
  224. pixmap = p
  225. print(f"✅ Loaded image: {path}")
  226. break
  227. if pixmap and not pixmap.isNull():
  228. left.setPixmap(pixmap.scaled(
  229. 280, 560,
  230. Qt.AspectRatioMode.KeepAspectRatio,
  231. Qt.TransformationMode.SmoothTransformation
  232. ))
  233. else:
  234. left.setText("📱\nCodex A12+\nActivation GUI")
  235. left.setStyleSheet("color: #e0e0e0; font-size: 16px; font-weight: bold;")
  236. layout.addWidget(left)
  237. # === Info & Control Panel ===
  238. right = QWidget()
  239. right.setFixedWidth(500)
  240. right_layout = QVBoxLayout(right)
  241. right_layout.setContentsMargins(40, 40, 40, 30)
  242. right_layout.setSpacing(14)
  243. layout.addWidget(right)
  244. title = QLabel("Codex A12+")
  245. title.setFont(QFont("SF Pro Display", 28, QFont.Weight.Bold))
  246. title.setStyleSheet("color: white;")
  247. right_layout.addWidget(title)
  248. subtitle = QLabel("Professional iOS Activation Bypass (A12+)")
  249. subtitle.setFont(QFont("SF Pro Display", 13))
  250. subtitle.setStyleSheet("color: #aaa;")
  251. right_layout.addWidget(subtitle)
  252. # Device info labels
  253. self.udid_label = QLabel("📱 UDID: —")
  254. self.ios_label = QLabel("🌐 iOS Version: —")
  255. self.device_label = QLabel("📱 Device Model: —")
  256. for lbl in [self.udid_label, self.ios_label, self.device_label]:
  257. lbl.setFont(QFont("SF Pro", 12))
  258. lbl.setStyleSheet("color: #ddd;")
  259. right_layout.addWidget(lbl)
  260. # Compatibility badge
  261. compat = QLabel("✅ Compatible: A12, A13, A14, A15, A16, A17 devices")
  262. compat.setFont(QFont("SF Pro", 11, QFont.Weight.Bold))
  263. compat.setStyleSheet("color: #00d188;")
  264. right_layout.addWidget(compat)
  265. # Log output
  266. self.log_view = QTextEdit()
  267. self.log_view.setReadOnly(True)
  268. self.log_view.setFont(QFont("Menlo", 11))
  269. self.log_view.setStyleSheet("""
  270. QTextEdit {
  271. background: #1e293b;
  272. color: #e2e8f0;
  273. border: 1px solid #334155;
  274. border-radius: 6px;
  275. padding: 8px;
  276. }
  277. """)
  278. self.log_view.setFixedHeight(120)
  279. right_layout.addWidget(self.log_view)
  280. # Progress & status
  281. self.progress = QProgressBar()
  282. self.progress.setRange(0, 100)
  283. self.progress.setValue(0)
  284. self.progress.setTextVisible(False)
  285. self.progress.setFixedHeight(6)
  286. self.progress.setStyleSheet("""
  287. QProgressBar {
  288. border: none;
  289. background: #334155;
  290. border-radius: 3px;
  291. }
  292. QProgressBar::chunk {
  293. background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
  294. stop:0 #4ade80, stop:1 #38bdf8);
  295. border-radius: 3px;
  296. }
  297. """)
  298. right_layout.addWidget(self.progress)
  299. self.status_label = QLabel("🔌 Connect device and press Start")
  300. self.status_label.setFont(QFont("SF Pro", 10))
  301. self.status_label.setStyleSheet("color: #94a3b8;")
  302. right_layout.addWidget(self.status_label)
  303. # Start button
  304. self.start_btn = QPushButton("🚀 Start Full Activation")
  305. self.start_btn.setFont(QFont("SF Pro", 15, QFont.Weight.Bold))
  306. self.start_btn.setFixedHeight(52)
  307. self.start_btn.setStyleSheet("""
  308. QPushButton {
  309. background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
  310. stop:0 #0ea5e9, stop:1 #0284c7);
  311. color: white;
  312. border: none;
  313. border-radius: 12px;
  314. }
  315. QPushButton:hover {
  316. background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
  317. stop:0 #0284c7, stop:1 #0369a1);
  318. }
  319. QPushButton:disabled {
  320. background: #334155;
  321. color: #64748b;
  322. }
  323. """)
  324. self.start_btn.clicked.connect(self.start_activation)
  325. right_layout.addWidget(self.start_btn)
  326. # Settings/about button
  327. self.about_btn = QPushButton("ℹ️ About")
  328. self.about_btn.setFixedSize(90, 32)
  329. self.about_btn.setStyleSheet("""
  330. QPushButton {
  331. background: transparent;
  332. color: #94a3b8;
  333. border: 1px solid #334155;
  334. border-radius: 6px;
  335. font-size: 12px;
  336. }
  337. QPushButton:hover {
  338. color: white;
  339. border-color: #4ade80;
  340. }
  341. """)
  342. self.about_btn.clicked.connect(self.show_about)
  343. right_layout.addWidget(self.about_btn, alignment=Qt.AlignmentFlag.AlignRight)
  344. # Auto-refresh timer
  345. self.check_timer = QTimer()
  346. self.check_timer.timeout.connect(self.check_device)
  347. self.check_timer.start(5000)
  348. QTimer.singleShot(1000, self.check_device)
  349. self.worker = None
  350. print("✅ MainWindow initialized")
  351. def paintEvent(self, event):
  352. painter = QPainter(self)
  353. grad = QLinearGradient(0, 0, self.width(), self.height())
  354. grad.setColorAt(0.0, QColor("#0f172a")) # slate-900
  355. grad.setColorAt(0.5, QColor("#1e293b")) # slate-800
  356. grad.setColorAt(1.0, QColor("#334155")) # slate-700
  357. painter.fillRect(self.rect(), grad)
  358. def log(self, msg: str):
  359. self.log_view.append(msg)
  360. self.log_view.verticalScrollBar().setValue(
  361. self.log_view.verticalScrollBar().maximum()
  362. )
  363. def check_device(self):
  364. try:
  365. auto = BypassAutomation(auto_confirm_guid=True)
  366. # Test ideviceinfo
  367. code, _, err = auto._run_cmd(["ideviceinfo", "--version"])
  368. if code != 0:
  369. self.status_label.setText("❌ ideviceinfo not available")
  370. self.start_btn.setEnabled(False)
  371. self.log("❌ ideviceinfo not found or failed")
  372. return
  373. # List devices
  374. code, out, err = auto._run_cmd(["idevice_id", "-l"])
  375. if code != 0 or not out.strip():
  376. self.status_label.setText("🔌 No device detected — connect & trust")
  377. self.start_btn.setEnabled(False)
  378. self.log("⏳ Waiting for USB device...")
  379. return
  380. udid = out.strip().split('\n')[0]
  381. code, out, _ = auto._run_cmd(["ideviceinfo", "-u", udid])
  382. if code == 0:
  383. info = {}
  384. for line in out.splitlines():
  385. if ": " in line:
  386. k, v = line.split(": ", 1)
  387. info[k.strip()] = v.strip()
  388. udid_short = info.get('UniqueDeviceID',)[:35] + "..."
  389. self.udid_label.setText(f"📱 UDID: {udid_short}")
  390. self.ios_label.setText(f"🌐 iOS: {info.get('ProductVersion', '?')}")
  391. self.device_label.setText(f"📱 Device: {info.get('ProductType', '?')}")
  392. self.status_label.setText("✅ Device ready — click Start to begin")
  393. self.start_btn.setEnabled(True)
  394. self.log("✅ Device detected and ready")
  395. else:
  396. self.status_label.setText("❌ Failed to read device info")
  397. self.start_btn.setEnabled(False)
  398. self.log("❌ ideviceinfo command failed")
  399. except Exception as e:
  400. self.udid_label.setText("📱 UDID: —")
  401. self.ios_label.setText("🌐 iOS Version: —")
  402. self.device_label.setText("📱 Device Model: —")
  403. self.status_label.setText("❌ Error during device check")
  404. self.start_btn.setEnabled(False)
  405. self.log(f"❌ Device check error: {e}")
  406. def start_activation(self):
  407. if self.worker and self.worker.isRunning():
  408. return
  409. self.log_view.clear()
  410. self.start_btn.setEnabled(False)
  411. self.start_btn.setText("⚡ Running Activation...")
  412. self.progress.setValue(0)
  413. self.status_label.setText("⚙️ Initializing bypass engine...")
  414. self.worker = WorkerThread()
  415. self.worker.log.connect(self.log)
  416. self.worker.device_info.connect(self.update_device_info)
  417. self.worker.progress.connect(self.progress.setValue)
  418. self.worker.finished.connect(self.on_finished)
  419. self.worker.start()
  420. def update_device_info(self, udid, ios, product):
  421. self.udid_label.setText(f"📱 UDID: {udid[:12]}...")
  422. self.ios_label.setText(f"🌐 iOS: {ios}")
  423. self.device_label.setText(f"📱 Device: {product}")
  424. def on_finished(self, success, msg):
  425. self.start_btn.setEnabled(True)
  426. self.start_btn.setText("🚀 Start Full Activation")
  427. if success:
  428. QMessageBox.information(
  429. self, "✅ Success",
  430. "Activation process completed successfully!\n\n"
  431. )
  432. self.status_label.setText("✅ Done — activation in progress")
  433. self.log("🎉 Activation completed successfully")
  434. else:
  435. QMessageBox.critical(
  436. self, "❌ Activation Failed",
  437. f"Error: {msg}\n\n"
  438. "Troubleshooting:\n"
  439. "• Ensure device is trusted and unlocked\n"
  440. "• Close iTunes/Finder\n"
  441. "• Use high-quality USB cable\n"
  442. "• Device must be in normal (not DFU/recovery) mode\n"
  443. "• Check Desktop/codex_app.log for details"
  444. )
  445. self.status_label.setText("❌ Activation failed")
  446. self.log("❌ Activation failed — see error above")
  447. def show_about(self):
  448. QMessageBox.about(
  449. self, "ℹ️ About Codex A12+",
  450. "<h3>Codex A12+ — iOS Activation Bypass Tool</h3>"
  451. "<p><b>Version:</b> 2.1 (GUI — Full Auto)</p>"
  452. "<p><b>Features:</b></p>"
  453. "<ul>"
  454. "<li>✅ Full auto GUID detection (no manual input)</li>"
  455. "<li>✅ Supports ifuse & pymobiledevice3 backends</li>"
  456. "<li>✅ A12–A17 device support</li>"
  457. "<li>✅ Built-in SSL bypass (<code>-k</code>)</li>"
  458. "</ul>"
  459. f"<p><b>Bundle Mode:</b> {'Yes' if getattr(sys, 'frozen', False) else 'No'}</p>"
  460. f"<p><b>Working Dir:</b> {os.getcwd()}</p>"
  461. "<p><i>For research and educational purposes only.</i></p>"
  462. "<p>© Codex Team — Developed by Rustam Asadov (Rust505)</p>"
  463. )
  464. def main():
  465. try:
  466. print("🚀 Starting Codex A12+ GUI...")
  467. Path("assets").mkdir(exist_ok=True)
  468. app = QApplication(sys.argv)
  469. if platform.system() == "Darwin":
  470. app.setStyle("macos")
  471. app.setFont(QFont("SF Pro Display", 13))
  472. window = MainWindow()
  473. window.show()
  474. print("✅ Application started")
  475. return app.exec()
  476. except Exception as e:
  477. print(f"💥 Fatal startup error: {e}")
  478. traceback.print_exc()
  479. try:
  480. app = QApplication(sys.argv)
  481. QMessageBox.critical(
  482. None,
  483. "❌ Startup Error",
  484. f"Codex A12+ failed to start:\n{str(e)}\n\n"
  485. "Check Desktop/codex_app.log for diagnostics."
  486. )
  487. except:
  488. pass
  489. return 1
  490. if __name__ == "__main__":
  491. sys.exit(main())