""" Debug Flash — chạy đầy đủ 3 bước flash và log chi tiết từng bước. Usage: python debug_full.py python debug_full.py 192.168.11.17 V3.0.6p5.bin """ import sys import os import requests import re def debug_flash(ip, firmware_path, username="root", password=""): base_url = f"http://{ip}" session = requests.Session() print(f"{'='*65}") print(f" Full Flash Debug — {ip}") print(f" Firmware: {os.path.basename(firmware_path)}") print(f" Size: {os.path.getsize(firmware_path) / 1024 / 1024:.2f} MB") print(f"{'='*65}") # ═══════════════════════════════════ # STEP 1: Login # ═══════════════════════════════════ print(f"\n{'─'*65}") print(f" STEP 1: Login") print(f"{'─'*65}") login_url = f"{base_url}/cgi-bin/luci" print(f" → GET {login_url}") try: r = session.get(login_url, timeout=10) print(f" Status: {r.status_code}") except Exception as e: print(f" ❌ Cannot connect: {e}") return # Detect form fields if 'name="luci_username"' in r.text: login_data = {"luci_username": username, "luci_password": password} print(f" Fields: luci_username / luci_password") else: login_data = {"username": username, "password": password} print(f" Fields: username / password") print(f"\n → POST {login_url}") print(f" Data: {login_data}") resp = session.post(login_url, data=login_data, timeout=10, allow_redirects=True) print(f" Status: {resp.status_code}") print(f" Cookies: {dict(session.cookies)}") # Extract stok stok = None for source in [resp.url, resp.text]: match = re.search(r";stok=([a-f0-9]+)", source) if match: stok = match.group(1) break if not stok: for hist in resp.history: match = re.search(r";stok=([a-f0-9]+)", hist.headers.get("Location", "")) if match: stok = match.group(1) break print(f" stok: {stok}") has_cookie = "sysauth" in str(session.cookies) print(f" sysauth: {'✅ YES' if has_cookie else '❌ NO'}") if not stok and not has_cookie: print(f"\n ❌ LOGIN FAILED — no stok, no sysauth cookie") return print(f"\n ✅ LOGIN OK") # Build flash URL if stok: flash_url = f"{base_url}/cgi-bin/luci/;stok={stok}/admin/system/flashops" else: flash_url = f"{base_url}/cgi-bin/luci/admin/system/flashops" # ═══════════════════════════════════ # STEP 2: Upload firmware # ═══════════════════════════════════ print(f"\n{'─'*65}") print(f" STEP 2: Upload firmware") print(f"{'─'*65}") print(f" → POST {flash_url}") print(f" File field: image") print(f" File name: {os.path.basename(firmware_path)}") print(f" keep: NOT sent (unchecked)") print(f" Uploading...") filename = os.path.basename(firmware_path) with open(firmware_path, "rb") as f: resp = session.post( flash_url, data={}, # No keep = uncheck Keep Settings files={"image": (filename, f, "application/octet-stream")}, timeout=300, ) print(f" Status: {resp.status_code}") print(f" URL: {resp.url}") # Check response content resp_lower = resp.text.lower() has_verify = "verify" in resp_lower has_proceed = "proceed" in resp_lower has_checksum = "checksum" in resp_lower has_image_form = 'name="image"' in resp.text and 'type="file"' in resp.text print(f"\n Response analysis:") print(f" Has 'verify': {'✅' if has_verify else '❌'}") print(f" Has 'proceed': {'✅' if has_proceed else '❌'}") print(f" Has 'checksum': {'✅' if has_checksum else '❌'}") print(f" Has flash form: {'⚠️ (upload ignored!)' if has_image_form else '✅ (not flash form)'}") # Extract checksum from verification page checksum = re.search(r'Checksum:\s*([a-f0-9]+)', resp.text) size_info = re.search(r'Size:\s*([\d.]+\s*MB)', resp.text) if checksum: print(f" Checksum: {checksum.group(1)}") if size_info: print(f" Size: {size_info.group(1)}") # Check for keep settings status if "will be erased" in resp.text: print(f" Config: Will be ERASED ✅ (keep=off)") elif "will be kept" in resp.text: print(f" Config: Will be KEPT ⚠️ (keep=on)") if not has_verify or not has_proceed: print(f"\n ❌ Did NOT get verification page!") # Show cleaned response text = re.sub(r'<[^>]+>', ' ', resp.text) text = re.sub(r'\s+', ' ', text).strip() print(f" Response: {text[:500]}") return # Show the Proceed form print(f"\n Proceed form (from HTML):") forms = re.findall(r']*>(.*?)', resp.text, re.DOTALL) for i, form_body in enumerate(forms): if 'value="Proceed"' in form_body or 'step' in form_body: inputs = re.findall(r']*/?\s*>', form_body) for inp in inputs: print(f" {inp.strip()}") print(f"\n ✅ UPLOAD OK — Verification page received") # ═══════════════════════════════════ # STEP 3: Proceed (confirm flash) # ═══════════════════════════════════ print(f"\n{'─'*65}") print(f" STEP 3: Proceed (confirm flash)") print(f"{'─'*65}") confirm_data = { "step": "2", "keep": "", } print(f" → POST {flash_url}") print(f" Data: {confirm_data}") try: resp = session.post(flash_url, data=confirm_data, timeout=300) print(f" Status: {resp.status_code}") # Show cleaned response text = re.sub(r'<[^>]+>', ' ', resp.text) text = re.sub(r'\s+', ' ', text).strip() print(f" Response: {text[:300]}") except requests.ConnectionError: print(f" ✅ Connection dropped — device is REBOOTING!") except requests.ReadTimeout: print(f" ✅ Timeout — device is REBOOTING!") print(f"\n{'='*65}") print(f" 🎉 FLASH COMPLETE") print(f"{'='*65}") if __name__ == "__main__": if len(sys.argv) < 3: print("Usage: python debug_full.py ") print("Example: python debug_full.py 192.168.11.17 V3.0.6p5.bin") sys.exit(1) ip = sys.argv[1] fw = sys.argv[2] if not os.path.exists(fw): print(f"❌ File not found: {fw}") sys.exit(1) debug_flash(ip, fw)