#!/usr/bin/env python3
"""
Aggressive iOS Device GUID Extractor
Attempts multiple methods to trigger GUID appearance in syslog
"""

import subprocess
import re
import sys
import signal
import time
import threading
from datetime import datetime

class AggressiveGUIDExtractor:
    def __init__(self):
        self.running = True
        self.guid_found = False
        self.guid_value = None
        # Multiple patterns to catch different variations
        self.patterns = [
            r'/private/var/containers/Shared/SystemGroup/([0-9A-Fa-f-]{8}-[0-9A-Fa-f-]{4}-[0-9A-Fa-f-]{4}-[0-9A-Fa-f-]{4}-[0-9A-Fa-f-]{12})/Documents/BLDatabaseManager',
            r'/private/var/containers/Shared/SystemGroup/([0-9A-Fa-f-]{8}-[0-9A-Fa-f-]{4}-[0-9A-Fa-f-]{4}-[0-9A-Fa-f-]{4}-[0-9A-Fa-f-]{12})',
            r'SystemGroup/([0-9A-Fa-f-]{8}-[0-9A-Fa-f-]{4}-[0-9A-Fa-f-]{4}-[0-9A-Fa-f-]{4}-[0-9A-Fa-f-]{12})',
        ]
        
    def log(self, message):
        """Print timestamped log message"""
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print(f"[{timestamp}] {message}")
        sys.stdout.flush()
    
    def signal_handler(self, sig, frame):
        """Handle Ctrl+C gracefully"""
        self.log("\nStopping monitor...")
        self.running = False
        sys.exit(0)
    
    def check_tools_available(self):
        """Check if required tools are available"""
        tools = ["idevicesyslog", "idevice_id", "ideviceinfo"]
        missing = []
        
        for tool in tools:
            result = subprocess.run(
                ["which", tool],
                capture_output=True,
                text=True
            )
            if result.returncode != 0:
                missing.append(tool)
        
        if missing:
            self.log(f"ERROR: Missing tools: {', '.join(missing)}")
            self.log("Install libimobiledevice:")
            self.log("  macOS: brew install libimobiledevice")
            self.log("  Linux: sudo apt-get install libimobiledevice-utils")
            return False
        return True
    
    def get_device_info(self):
        """Get device information"""
        try:
            result = subprocess.run(
                ["ideviceinfo"],
                capture_output=True,
                text=True,
                timeout=10
            )
            
            if result.returncode == 0:
                info = {}
                for line in result.stdout.split('\n'):
                    if ':' in line:
                        key, value = line.split(':', 1)
                        info[key.strip()] = value.strip()
                
                self.log(f"Device Name: {info.get('DeviceName', 'Unknown')}")
                self.log(f"Product Type: {info.get('ProductType', 'Unknown')}")
                self.log(f"iOS Version: {info.get('ProductVersion', 'Unknown')}")
                self.log(f"Activation State: {info.get('ActivationState', 'Unknown')}")
                
                return info
            else:
                self.log("Could not retrieve device info")
                return None
        except Exception as e:
            self.log(f"Error getting device info: {e}")
            return None
    
    def trigger_device_activity(self):
        """Try to trigger device activity that might generate logs"""
        self.log("Attempting to trigger device activity...")
        
        triggers = [
            ("idevicediagnostics", ["restart"], "Attempting device restart trigger"),
            ("idevicediagnostics", ["sleep"], "Attempting sleep trigger"),
            ("ideviceinfo", ["-q", "com.apple.mobile.battery"], "Querying battery info"),
            ("ideviceinfo", ["-q", "com.apple.disk_usage"], "Querying disk usage"),
            ("idevicecrashreport", ["-e"], "Checking crash reports"),
        ]
        
        for tool, args, desc in triggers:
            try:
                self.log(f"  → {desc}")
                subprocess.run(
                    [tool] + args,
                    capture_output=True,
                    text=True,
                    timeout=5
                )
                time.sleep(1)
            except Exception as e:
                # Silent fail, just try the next one
                pass
    
    def periodic_trigger(self):
        """Periodically trigger device queries to generate activity"""
        while self.running and not self.guid_found:
            time.sleep(30)  # Every 30 seconds
            if not self.guid_found:
                self.log("Sending periodic device queries...")
                try:
                    subprocess.run(
                        ["ideviceinfo", "-s"],
                        capture_output=True,
                        timeout=5
                    )
                except:
                    pass
    
    def extract_guid_from_line(self, line):
        """Extract GUID from a syslog line using multiple patterns"""
        for pattern in self.patterns:
            match = re.search(pattern, line, re.IGNORECASE)
            if match:
                return match.group(1).upper()
        return None
    
    def monitor_syslog(self):
        """Continuously monitor idevicesyslog output"""
        self.log("Starting aggressive syslog monitoring...")
        self.log("-" * 60)
        
        # Start periodic trigger thread
        trigger_thread = threading.Thread(target=self.periodic_trigger, daemon=True)
        trigger_thread.start()
        
        process = None
        try:
            # Start idevicesyslog process
            process = subprocess.Popen(
                ["idevicesyslog"],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                bufsize=1,
                universal_newlines=True
            )
            
            line_count = 0
            last_report = time.time()
            
            # Read output line by line
            for line in process.stdout:
                if not self.running or self.guid_found:
                    break
                
                line_count += 1
                
                # Show progress every 5 seconds
                current_time = time.time()
                if current_time - last_report >= 5:
                    self.log(f"Monitoring... ({line_count} lines processed)")
                    last_report = current_time
                
                # Print any line that contains "SystemGroup" or "BLDatabase"
                if 'SystemGroup' in line or 'BLDatabase' in line or 'ScreenTime' in line:
                    self.log(f"INTERESTING: {line.strip()[:200]}")
                
                # Check if this line contains our GUID
                guid = self.extract_guid_from_line(line)
                
                if guid:
                    self.guid_found = True
                    self.guid_value = guid
                    self.log("=" * 60)
                    self.log("✓ SUCCESS! GUID FOUND!")
                    self.log(f"✓ GUID: {guid}")
                    self.log("=" * 60)
                    self.log(f"Full line: {line.strip()}")
                    self.log("=" * 60)
                    
                    # Terminate the process
                    process.terminate()
                    return guid
            
            # If we get here, the process ended without finding the GUID
            process.wait()
            self.log("idevicesyslog process ended")
            return None
            
        except KeyboardInterrupt:
            self.log("\nInterrupted by user")
            if process:
                process.terminate()
            return None
        except Exception as e:
            self.log(f"ERROR during monitoring: {e}")
            if process:
                process.terminate()
            return None
    
    def try_backup_method(self):
        """Try to extract GUID from device backup or other sources"""
        self.log("\nAttempting alternative methods...")
        
        # Try to list app containers
        try:
            self.log("Attempting to list device containers...")
            result = subprocess.run(
                ["ideviceimagemounter", "-l"],
                capture_output=True,
                text=True,
                timeout=10
            )
            self.log(f"Image mounter output: {result.stdout[:200]}")
        except:
            pass
        
        # Try to access file system if available
        try:
            self.log("Attempting to access file system...")
            result = subprocess.run(
                ["ifuse", "--list-apps"],
                capture_output=True,
                text=True,
                timeout=10
            )
            for line in result.stdout.split('\n'):
                guid = self.extract_guid_from_line(line)
                if guid:
                    self.log(f"Found GUID in file system: {guid}")
                    return guid
        except:
            pass
        
        return None
    
    def run(self):
        """Main run method"""
        signal.signal(signal.SIGINT, self.signal_handler)
        
        self.log("Checking for required tools...")
        if not self.check_tools_available():
            return None
        
        self.log("\nGetting device information...")
        device_info = self.get_device_info()
        
        if device_info:
            activation_state = device_info.get('ActivationState', 'Unknown')
            if activation_state == 'Unactivated':
                self.log("\n⚠️  WARNING: Device is not activated!")
                self.log("The GUID may not exist yet on an unactivated device.")
                self.log("Attempting anyway, but success is unlikely...")
        
        self.log("\nTriggering initial device activity...")
        self.trigger_device_activity()
        
        self.log("\nStarting continuous monitoring...")
        self.log("This will run indefinitely until GUID is found or you press Ctrl+C")
        self.log("")
        
        guid = self.monitor_syslog()
        
        if not guid:
            self.log("\nGUID not found in syslog, trying alternative methods...")
            guid = self.try_backup_method()
        
        if guid:
            self.log("\n✓ Extraction complete!")
            return guid
        else:
            self.log("\n✗ Unable to extract GUID")
            self.log("\nPossible reasons:")
            self.log("  1. Device is not activated (most likely)")
            self.log("  2. Screen Time has never been accessed")
            self.log("  3. iOS version doesn't use this path")
            self.log("\nRecommendation: Activate the device and access Screen Time")
            return None

def main():
    print("=" * 60)
    print("Aggressive iOS Device GUID Extractor")
    print("=" * 60)
    print()
    print("This script will:")
    print("  • Monitor device syslog continuously")
    print("  • Trigger device activity to generate logs")
    print("  • Try alternative methods to find the GUID")
    print()
    print("Requirements:")
    print("  • iOS device connected via USB")
    print("  • Device trusted on this computer")
    print()
    print("Starting in 3 seconds...")
    print()
    time.sleep(3)
    
    extractor = AggressiveGUIDExtractor()
    guid = extractor.run()
    
    if guid:
        print()
        print(f"SUCCESS! Your GUID is: {guid}")
        print()
        sys.exit(0)
    else:
        sys.exit(1)

if __name__ == "__main__":
    main()
