# ⚡ Mira Firmware Loader Công cụ desktop dùng để **scan, phát hiện và flash firmware hàng loạt** cho các thiết bị OpenWrt trong mạng LAN. Hỗ trợ nạp **thủ công** (chọn thiết bị → flash) và **tự động hóa** (scan → phát hiện → flash → retry). > **Tech stack:** Python 3.9+ · PyQt6 · Paramiko/SCP · Scapy · Requests · PyInstaller > **Phiên bản:** `1.2.0` --- ## 📁 Cấu trúc dự án ```text Mira_Firmware_Loader/ ├── main.py # UI chính (PyQt6) — App + AutoFlashWindow ├── version.txt # Số phiên bản ứng dụng ├── requirements.txt # Danh sách dependencies ├── core/ │ ├── scanner.py # Quét thiết bị mạng đa lớp (Ping sweep + ARP + Scapy) │ ├── workers.py # ScanThread — chạy scanner trong background thread │ ├── api_flash.py # Flash firmware qua LuCI HTTP API │ ├── ssh_utils.py # SSH/SCP transport helpers dùng chung │ ├── ssh_new_flash.py # Luồng SSH cho chế độ Nạp Mới (Telnet → set passwd → SSH) │ ├── ssh_update_flash.py # Luồng SSH cho chế độ Update (SSH trực tiếp) │ ├── flash_new_worker.py # NewFlashThread — QThread điều phối Nạp Mới FW │ ├── flash_update_worker.py # UpdateFlashThread — QThread điều phối Update FW │ └── auto_flash_worker.py # AutoFlashWorker — QThread tự động scan → flash → retry ├── ui/ │ ├── components.py # Custom Qt Widgets (CollapsibleGroupBox) │ └── styles.py # Stylesheet toàn ứng dụng (STYLE + AUTO_STYLE) ├── utils/ │ ├── network.py # Helper IP / network (get_local_ip, get_default_network) │ └── system.py # Lấy thông tin máy, resource path cho PyInstaller ├── docs/ │ ├── api_flash_docs.md # Tài liệu kỹ thuật LuCI API flash │ ├── load_fw_ssh_docs.md # Tài liệu kỹ thuật SSH flash (cả 2 luồng) │ ├── scanner_docs.md # Tài liệu kỹ thuật scanner │ └── auto_flash_docs.md # Tài liệu kỹ thuật tự động hóa nạp FW ├── run.sh # Script khởi chạy (macOS/Linux) ├── run.bat # Script khởi chạy (Windows) └── build_windows.bat # Script đóng gói thành .exe (Windows, PyInstaller) ``` --- ## 🔧 Cài đặt & Chạy ### Yêu cầu - Python **3.9+** - Thư viện: `PyQt6`, `scapy`, `requests`, `paramiko`, `scp`, `pyinstaller` (để build) - _Windows:_ Cài [Npcap](https://npcap.com/) để Scapy có thể gửi ARP broadcast (không bắt buộc — có fallback) ### Khởi chạy nhanh **macOS / Linux:** ```bash ./run.sh ``` **Windows:** ```bat run.bat ``` > Script tự tạo `venv` và cài dependencies nếu chưa có. ### 📦 Build file chạy độc lập (.exe) cho Windows ```bat build_windows.bat ``` Output: `dist\Mira_Firmware_Loader.exe` — không cần cài Python trên máy đích. --- ## 🏗 Kiến trúc hệ thống ``` ┌──────────────────────────────────────────────────────────┐ │ main.py (UI) │ │ │ │ Machine Info │ Firmware Selector │ Network Scan │ │ ───────────────────────────────────────────────────── │ │ Device Table [ ] IP │ MAC │ Status │ │ ───────────────────────────────────────────────────── │ │ Flash Controls │ │ Flash Mode: [ New Flash | Update Firmware ] │ │ Method: [ API (LuCI) | SSH (paramiko) ] │ │ Concurrent devices: [SpinBox] │ │ [ ⚡ FLASH SELECTED DEVICES ] │ │ ───────────────────────────────────────────────────── │ │ [ 🤖 Tự động hóa nạp FW ] │ └────────────────────────┬─────────────────────────────────┘ │ ┌─────────────────┼─────────────────┐ │ │ │ ┌──────▼──────┐ ┌───────▼──────────┐ ┌───▼──────────────────┐ │ scanner.py │ │ Flash Workers │ │ AutoFlashWorker │ │ │ │ (thủ công) │ │ (tự động hóa) │ │ 1. Ping │ │ │ │ │ │ Sweep │ │ NewFlashThread │ │ Phase 1: Scan LAN │ │ 2. arp -a │ │ ├─ api_flash │ │ (tối đa 15 lần) │ │ 3. Scapy │ │ └─ ssh_new_flash│ │ Phase 2: Flash │ │ ARP │ │ │ │ (auto-retry x3) │ └─────────────┘ │ UpdateFlash │ │ ├─ api_flash │ │ Thread │ │ └─ ssh_new_flash │ │ └─ ssh_update │ └──────────────────────┘ └──────────────────┘ │ ┌──────────▼──────────┐ │ ssh_utils.py │ │ (Transport Layer) │ │ _create_ssh_client │ │ _upload_firmware │ │ _verify_firmware │ │ _sync_and_sysupgr │ └─────────────────────┘ ``` --- ## 🔄 Luồng hoạt động chi tiết ### 1. Quét mạng (Network Scan) `scanner.py` dùng chiến lược 3 lớp, đảm bảo phát hiện đầy đủ mà không cần quyền Root: | Giai đoạn | Mô tả | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Ping Sweep** | Gửi ping đồng thời tới toàn bộ host trong dải `/24` (tất cả cùng lúc, không batching) để đánh thức thiết bị và điền ARP cache | | **ARP Table** | Đọc `arp -a` bằng regex, hỗ trợ cả định dạng Windows (`cc-2d-...`) và macOS/Linux (`aa:bb:...`) | | **Scapy ARP** | Chạy **song song** với ARP Table — gửi ARP broadcast để lấp khoảng trống. Yêu cầu Npcap (Windows) hoặc root (Linux); tự động bỏ qua nếu không khả dụng | Kết quả được merge theo IP và sort tăng dần trước khi trả về UI. ### 2. Bảng thiết bị (Device Table) - Hiển thị cột: checkbox, IP, MAC, Status - Mặc định ẩn gateway và IP máy tính đang chạy (có thể bật "Show all") - Thiết bị đã flash trong session được đánh dấu "Already Flashed" và tự bỏ tick ### 3. Flash Firmware (Thủ công) Có 2 chế độ và 2 method, tổng cộng 3 luồng thực thi khác nhau: #### Chế độ `New Flash` — dùng cho thiết bị vừa reset cứng | Method | Luồng | Mô tả | | -------------- | ------------------ | ------------------------------------------------------------------------------------------- | | **API (LuCI)** | `api_flash.py` | Đăng nhập LuCI → upload firmware → Proceed. Hỗ trợ Barrier Breaker 14.07 và OpenWrt mới hơn | | **SSH** | `ssh_new_flash.py` | Kết nối Telnet → đặt password mới → SSH → SCP upload → sysupgrade | **Luồng SSH – New Flash chi tiết:** 1. Telnet port 23 (thiết bị vừa reset, chưa có pass) 2. Đặt password `admin123a` qua lệnh `passwd` 3. SSH vào với password vừa đặt 4. SCP upload firmware lên `/tmp/` 5. Verify file tồn tại 6. `sync && sysupgrade -F -v -n` → thiết bị reboot (connection drop = DONE) #### Chế độ `Update Firmware` — dùng cho thiết bị đang chạy OpenWrt Luồng `ssh_update_flash.py`, SSH trực tiếp (không qua Telnet): 1. SSH kết nối với `root` / `admin123a` (fallback: `admin`) 2. SCP upload firmware lên `/tmp/` 3. Verify file tồn tại 4. `sync && sysupgrade -F -v -n` → thiết bị reboot > ⚠️ Update Mode hiển thị cảnh báo nếu IP thiết bị khác `192.168.11.102` và yêu cầu xác nhận. ### 4. 🤖 Tự động hóa nạp FW (MỚI) Tính năng nạp FW hoàn toàn tự động — chỉ cần cấu hình và nhấn bắt đầu: ``` Cấu hình → Auto Scan LAN → Phát hiện đủ thiết bị → Auto Flash → Auto Retry → Thông báo ``` #### 4.1. Quy trình | Phase | Mô tả | | --------------------- | ------------------------------------------------------------------------- | | **Phase 1: Scan LAN** | Scan mạng liên tục mỗi 5 giây, tối đa **15 lần**, cho đến khi đủ thiết bị | | **Phase 2: Flash** | Nạp FW song song qua ThreadPoolExecutor, tự động **retry tối đa 3 lần** | #### 4.2. Cấu hình | Tham số | Mô tả | Mặc định | | --------------- | --------------------------------------------- | ------------------- | | **Firmware** | File firmware (.bin/.hex/.uf2) | Lấy từ cửa sổ chính | | **Mạng** | Dải mạng LAN cần scan | Tự suy từ IP host | | **Số lượng** | Số thiết bị cần nạp | 5 | | **Phương thức** | API (LuCI) hoặc SSH | API (LuCI) | | **Song song** | Số thiết bị nạp cùng lúc (0 = không giới hạn) | 10 | #### 4.3. Auto-Retry nạp FW Khi một thiết bị nạp thất bại, hệ thống tự động retry: ``` Lần 1 → FAIL: Connection timeout ⚠️ Log cảnh báo, chờ 2 giây... Lần 2 → FAIL: Upload error ⚠️ Log cảnh báo, chờ 2 giây... Lần 3 → DONE ✅ (hoặc ❌ báo lỗi sau 3 lần) ``` - Tối đa **3 lần retry** mỗi thiết bị (`MAX_FLASH_RETRIES`) - Chờ **2 giây** giữa mỗi lần retry để thiết bị ổn định - Nếu hết 3 lần vẫn fail → đánh dấu ❌, tiếp tục thiết bị tiếp theo #### 4.4. Scan Timeout - Nếu scan **15 lần** mà chưa đủ thiết bị → dừng và hiện cảnh báo - Gợi ý kiểm tra: thiết bị đã bật chưa, dải mạng có đúng không #### 4.5. Quy tắc bảo vệ IP `192.168.11.102` | Chế độ | Được nạp 192.168.11.102? | Cơ chế | | ------------------------ | :----------------------: | ----------------------------------- | | **New Flash (thủ công)** | ❌ Không | Chặn trước khi flash, hiện cảnh báo | | **Update FW (thủ công)** | ✅ Có | Cho phép bình thường | | **Tự động hóa** | ❌ Không | Tự động lọc khỏi kết quả scan | #### 4.6. Lịch sử nạp Kết quả nạp được lưu ở 2 cấp: | Nơi lưu | Phạm vi | Format | | ------------------------------- | ---------------------- | ------------------------------------ | | `AutoFlashWindow._auto_history` | Phiên tự động hiện tại | `list[(ip, mac, result, timestamp)]` | | `App.flashed_macs` | Toàn bộ session app | `dict{MAC: (ip, mac, result, ts)}` | - Cả thành công ✅ lẫn thất bại ❌ đều được ghi lại - Nút "📋 Lịch sử nạp" hiển thị cùng format ở cả 2 cửa sổ: `[HH:MM:SS] ✅/❌ IP (MAC) — result` ### 5. Xử lý song song | Tham số | Mô tả | | ---------------------- | ------------------------------------------------------------------------ | | **Concurrent devices** | Số thiết bị flash đồng thời (`ThreadPoolExecutor`). `0` = không giới hạn | --- ## ⚙️ Cấu hình mặc định | Tham số | Giá trị mặc định | Mô tả | | ---------------------- | ------------------------------------ | --------------------------------- | | **Network** | Tự suy ra từ local IP (`x.x.x.0/24`) | Dải mạng để quét | | **Flash Mode** | `New Flash` | Nạp mới hoặc Update | | **Method** | `API (LuCI)` | Phương thức flash cho New Flash | | **SSH User** | `root` | Hardcoded, không hiển thị trên UI | | **SSH Password** | `admin123a` | Hardcoded, không hiển thị trên UI | | **Concurrent devices** | `10` | Số luồng flash song song | | **Show all** | Tắt | Ẩn gateway và IP máy host | | **MAX_FLASH_RETRIES** | `3` | Số lần retry nạp FW (tự động) | | **MAX_SCAN_ROUNDS** | `15` | Số lần scan tối đa (tự động) | --- ## 🛡️ Xử lý lỗi tổng hợp | Tình huống | Hành vi | | -------------------------------- | --------------------------------------------------- | | Scan exception (network error) | Log lỗi, chờ 3s, scan lại (đếm vào MAX_SCAN_ROUNDS) | | Scan 15 lần chưa đủ thiết bị | Hiện popup cảnh báo, dừng tự động | | Flash thất bại lần 1–2 (tự động) | Log cảnh báo, chờ 2s, retry tự động | | Flash thất bại sau 3 lần retry | Log lỗi, đánh dấu ❌, tiếp tục device tiếp theo | | User nhấn DỪNG | Set stop flag, dừng scan/flash an toàn | | IP 192.168.11.102 + New Flash | Chặn ngay, hiện cảnh báo | | Chưa chọn firmware | Hiện popup cảnh báo, không cho bắt đầu | | Mạng không hợp lệ | Hiện popup cảnh báo, không cho bắt đầu | --- ## 🔒 Lưu ý bảo mật & Quyền - **Scapy (chế độ sâu):** Cần Npcap (Windows) hoặc `sudo` (macOS/Linux). App vẫn hoạt động mà không cần quyền Admin nhờ fallback Ping Sweep + `arp -a`. - **CREATE_NO_WINDOW:** Khi gọi subprocess (`ping`, `arp`), ứng dụng dùng flag `CREATE_NO_WINDOW` trên Windows để ngăn cửa sổ console hiện ra. - **HTTP thuần:** Firmware được upload qua HTTP (không HTTPS). Chỉ dùng trong mạng LAN nội bộ, không nên dùng trên mạng mở. - **Credentials cố định:** SSH credentials (`root`/`admin123a`) được hardcode trong `flash_update_worker.py` và truyền từ `main.py`, không hiển thị trên UI.