|
|
|
@@ -0,0 +1,143 @@
|
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""
|
|
|
|
|
im literally vibecoding/google-fuing this shit i fucking hate python whitespace sdhould be meaningless
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from scapy.all import *
|
|
|
|
|
import requests
|
|
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
# Load webhook URL from file
|
|
|
|
|
try:
|
|
|
|
|
with open("webhook.txt", "r") as f:
|
|
|
|
|
DISCORD_WEBHOOK_URL = f.read().strip()
|
|
|
|
|
if not DISCORD_WEBHOOK_URL:
|
|
|
|
|
raise ValueError("Webhook URL is empty")
|
|
|
|
|
except FileNotFoundError:
|
|
|
|
|
print("Error: webhook.txt not found. Please create it with your Discord webhook URL.")
|
|
|
|
|
exit(1)
|
|
|
|
|
except ValueError as e:
|
|
|
|
|
print(f"Error: {e}")
|
|
|
|
|
exit(1)
|
|
|
|
|
|
|
|
|
|
# yee just remove the colons from the vendor id and prepend with 0x ig
|
|
|
|
|
VENDOR_IDS = [
|
|
|
|
|
0x0025DF,
|
|
|
|
|
0x005828,
|
|
|
|
|
0x00C0D4,
|
|
|
|
|
0x847003
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
# Cooldown to prevent spam notifications (seconds)
|
|
|
|
|
NOTIFICATION_COOLDOWN = 60
|
|
|
|
|
|
|
|
|
|
# Track last notification times per vendor ID
|
|
|
|
|
last_notifications = {}
|
|
|
|
|
|
|
|
|
|
def send_discord_notification(vendor_id, rssi=None):
|
|
|
|
|
"""Send a notification to Discord webhook"""
|
|
|
|
|
current_time = time.time()
|
|
|
|
|
|
|
|
|
|
# Check cooldown
|
|
|
|
|
if vendor_id in last_notifications:
|
|
|
|
|
if current_time - last_notifications[vendor_id] < NOTIFICATION_COOLDOWN:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
last_notifications[vendor_id] = current_time
|
|
|
|
|
|
|
|
|
|
# Format the message
|
|
|
|
|
vendor_names = {
|
|
|
|
|
0x004C: "Apple",
|
|
|
|
|
0x0006: "Microsoft",
|
|
|
|
|
0x0059: "Nordic Semiconductor",
|
|
|
|
|
0x0499: "Texas Instruments"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vendor_name = vendor_names.get(vendor_id, f"Unknown (0x{vendor_id:04X})")
|
|
|
|
|
|
|
|
|
|
message = f"🚨 BTLE Device Detected!\n**Vendor:** {vendor_name}\n**Vendor ID:** 0x{vendor_id:04X}"
|
|
|
|
|
if rssi:
|
|
|
|
|
message += f"\n**RSSI:** {rssi} dBm"
|
|
|
|
|
|
|
|
|
|
data = {
|
|
|
|
|
"content": message,
|
|
|
|
|
"username": "BTLE Sniffer",
|
|
|
|
|
"avatar_url": "https://i.imgur.com/4M34hi2.png" # Optional bot avatar
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
response = requests.post(DISCORD_WEBHOOK_URL, json=data)
|
|
|
|
|
if response.status_code == 204:
|
|
|
|
|
print(f"✅ Notification sent for vendor ID 0x{vendor_id:04X}")
|
|
|
|
|
else:
|
|
|
|
|
print(f"❌ Failed to send notification: {response.status_code}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ Error sending notification: {e}")
|
|
|
|
|
|
|
|
|
|
def parse_advertisement_data(data):
|
|
|
|
|
"""Parse BTLE advertisement data for manufacturer specific data"""
|
|
|
|
|
vendor_ids_found = []
|
|
|
|
|
i = 0
|
|
|
|
|
|
|
|
|
|
while i < len(data):
|
|
|
|
|
if i + 1 >= len(data):
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
length = data[i]
|
|
|
|
|
if length == 0:
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
if i + length + 1 > len(data):
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
ad_type = data[i + 1]
|
|
|
|
|
ad_data = data[i + 2:i + length + 1]
|
|
|
|
|
|
|
|
|
|
if ad_type == 0xFF and len(ad_data) >= 2: # Manufacturer Specific Data
|
|
|
|
|
company_id = int.from_bytes(ad_data[:2], 'little')
|
|
|
|
|
vendor_ids_found.append(company_id)
|
|
|
|
|
|
|
|
|
|
i += length + 1
|
|
|
|
|
|
|
|
|
|
return vendor_ids_found
|
|
|
|
|
|
|
|
|
|
def packet_callback(pkt):
|
|
|
|
|
"""Callback function for each captured packet"""
|
|
|
|
|
if pkt.haslayer(BTLE_ADV):
|
|
|
|
|
adv_layer = pkt[BTLE_ADV]
|
|
|
|
|
|
|
|
|
|
# Parse advertisement data
|
|
|
|
|
vendor_ids_found = parse_advertisement_data(adv_layer.data)
|
|
|
|
|
|
|
|
|
|
# Check for our target vendor IDs
|
|
|
|
|
for vendor_id in vendor_ids_found:
|
|
|
|
|
if vendor_id in VENDOR_IDS:
|
|
|
|
|
rssi = getattr(pkt, 'rssi', None)
|
|
|
|
|
print(f"🎯 Detected target vendor ID: 0x{vendor_id:04X}")
|
|
|
|
|
send_discord_notification(vendor_id, rssi)
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
"""Main function"""
|
|
|
|
|
print("🚀 Starting BTLE sniffer...")
|
|
|
|
|
print(f"📡 Monitoring for vendor IDs: {[f'0x{vid:04X}' for vid in VENDOR_IDS]}")
|
|
|
|
|
print("📢 Notifications will be sent to Discord webhook")
|
|
|
|
|
print("⚠️ Make sure you have the necessary permissions and compatible hardware")
|
|
|
|
|
print("Press Ctrl+C to stop\n")
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
# Note: On Linux/WSL, the interface might be 'hci0' or similar
|
|
|
|
|
# On Windows native, Bluetooth sniffing may not be supported
|
|
|
|
|
# You may need to adjust the interface name
|
|
|
|
|
sniff(iface="hci0", prn=packet_callback, store=0, filter="btle")
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
print("\n🛑 Sniffer stopped by user")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"❌ Error starting sniffer: {e}")
|
|
|
|
|
print("💡 Make sure:")
|
|
|
|
|
print(" - You have root/admin privileges")
|
|
|
|
|
print(" - Scapy is installed (pip install scapy)")
|
|
|
|
|
print(" - Your Bluetooth adapter supports raw packet capture")
|
|
|
|
|
print(" - The interface name is correct (try 'hciconfig' to list interfaces)")
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|