update README.md

This commit is contained in:
2026-03-09 13:19:00 +07:00
parent 564b2567b6
commit 594d13c0cc

216
README.md
View File

@@ -1,31 +1,41 @@
# ⚡ IoT Firmware Loader (MiraV3) # ⚡ 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 (qua giao diện LuCI) trong mạng LAN. 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.
> **Tech stack:** Python 3.9+ · PyQt6 · Scapy · Requests · PyInstaller > **Tech stack:** Python 3.9+ · PyQt6 · Paramiko/SCP · Scapy · Requests · PyInstaller
> **Phiên bản:** `1.1.2`
--- ---
## 📁 Cấu trúc dự án ## 📁 Cấu trúc dự án
```text ```text
iot_fw_loader/ Mira_Firmware_Loader/
├── main.py # UI chính (PyQt6) ├── main.py # UI chính (PyQt6)
├── core/ # Các thành phần cốt lõi và xử lý đa luồng ├── version.txt # Số phiên bản ứng dụng
│ ├── workers.py # Quản lý luồng dùng chung (ScanThread, FlashThread) ├── requirements.txt # Danh sách dependencies
│ ├── scanner.py # Quét thiết bị mạng đa lớp (Ping sweep + ARP + Scapy) ├── core/
│ ├── flasher.py # Flash firmware và tự động hóa qua OpenWrt LuCI bằng API │ ├── scanner.py # Quét thiết bị mạng đa lớp (Ping sweep + ARP + Scapy)
── ssh_flasher.py # Load/Update firmware qua đường dẫn SSH ── workers.py # ScanThread — chạy scanner trong background thread
├── ui/ # Các component thiết kế giao diện │ ├── api_flash.py # Flash firmware qua LuCI HTTP API
│ ├── components.py # Custom Qt Widgets (CollapsibleGroupBox, etc.) │ ├── ssh_utils.py # SSH/SCP transport helpers dùng chung
── styles.py # Các cấu hình Stylesheet ── ssh_new_flash.py # Luồng SSH cho chế độ Nạp Mới (Telnet → set passwd → SSH)
├── utils/ # Các hàm helper tiện ích │ ├── ssh_update_flash.py # Luồng SSH cho chế độ Update (SSH trực tiếp)
│ ├── network.py # Các hàm xử lý IP, Hostname │ ├── flash_new_worker.py # NewFlashThread — QThread điều phối Nạp Mới FW
│ └── system.py # Các hàm lấy thông tin máy và resources │ └── flash_update_worker.py # UpdateFlashThread — QThread điều phối Update FW
├── run.sh # Script khởi chạy (macOS/Linux) ├── ui/
├── run.bat # Script khởi chạy (Windows) │ ├── components.py # Custom Qt Widgets (CollapsibleGroupBox)
├── build_windows.bat # Script đóng gói thành file .exe độc lập (Windows) │ └── styles.py # Stylesheet toàn ứng dụng
── venv/ # Python virtual environment (tự tạo) ── 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
├── 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)
``` ```
--- ---
@@ -34,11 +44,11 @@ iot_fw_loader/
### Yêu cầu ### Yêu cầu
- Python 3.9+ - Python **3.9+**
- Các thư viện: `PyQt6`, `scapy`, `requests`, `pyinstaller` (để build). - Thư viện: `PyQt6`, `scapy`, `requests`, `paramiko`, `scp`, `pyinstaller` (để build)
- _Trên Windows:_ Cần cài đặt [Npcap](https://npcap.com/) để `scapy` có thể quét ARP ở chế độ sâu (không bắt buộc, có fallback dự phòng). - _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 (Môi trường Dev) ### Khởi chạy nhanh
**macOS / Linux:** **macOS / Linux:**
@@ -54,48 +64,56 @@ run.bat
> Script tự tạo `venv` và cài dependencies nếu chưa có. > Script tự tạo `venv` và cài dependencies nếu chưa có.
### 📦 Build ra file chạy độc lập (.exe) cho Windows ### 📦 Build file chạy độc lập (.exe) cho Windows
Chạy script sau để tự động đóng gói ứng dụng thành 1 file `.exe` duy nhất (không cần cài Python trên máy đích):
```bat ```bat
build_windows.bat build_windows.bat
``` ```
File nhận được sẽ nằm ở: `dist\IoT_Firmware_Loader.exe`. 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 ## 🏗 Kiến trúc hệ thống
```text ```
┌───────────────────────────────────────────────────────────── ┌──────────────────────────────────────────────────────────┐
│ main.py (UI) main.py (UI) │
┌───────────┐ ┌───────────┐ ┌────────────────────────────┐
Machine │ Firmware │ Network Scan (QThread) │ Machine Info │ Firmware Selector │ Network Scan
│ Info │ │ Selector │ │ Tham số: Network (CIDR) │ ─────────────────────────────────────────────────────
└───────────┘ └───────────┘ └────────┬───────────────────┘ Device Table [ ] IP │ MAC │ Status
────────────────────────────────────┼─────────────────── │ ─────────────────────────────────────────────────────
│ Device Table Flash Controls
[ ] IP │ Name │ MAC │ Status │ Flash Mode: [ New Flash | Update Firmware ]
(Hỗ trợ lọc ẩn Gateway & Self) │ Method: [ API (LuCI) | SSH (paramiko) ]
└────────────────────────────────────┼───────────────────┘ Concurrent devices: [SpinBox]
┌────────────────────────────────────┼───────────────────┐ [ ⚡ FLASH SELECTED DEVICES ]
│ │ Flash Controls + Progress Bar │ │ │ └────────────────────────┬─────────────────────────────────┘
(ThreadPoolExecutor) │
└────────────────────────────────────┼───────────────────┘ │ ──────────────┼──────────────
└───────────────────────────────────────┼─────────────────────┘ │ │
┌──────▼──────┐ ┌────────▼────────────────┐
┌───────────────────┼───────────────────┐ scanner.py │ Flash Workers │
┌─────▼─────┐ ┌─────▼─────┐ 1. Ping NewFlashThread │
│ scanner.py│ │ flasher.py Sweep │ │ ├─ method=api
│ │ │ │ 2. arp -a │ │ │ └── api_flash.py
│ 1. Ping │ LuCI HTTP 3. Scapy └─ method=ssh
Sweep │ /cgi-bin/ ARP └── ssh_new_flash
│ 2. arp -a │ luci └─────────────┘ │ │
│ 3. Scapy │ UpdateFlashThread
└───────────┘ └───────────┘ └─ ssh_update_flash.py │
└─────────────────────────┘
┌──────────▼──────────┐
│ ssh_utils.py │
│ (Transport Layer) │
│ _create_ssh_client │
│ _upload_firmware │
│ _verify_firmware │
│ _sync_and_sysupgr │
└─────────────────────┘
``` ```
--- ---
@@ -104,44 +122,78 @@ File nhận được sẽ nằm ở: `dist\IoT_Firmware_Loader.exe`.
### 1. Quét mạng (Network Scan) ### 1. Quét mạng (Network Scan)
Module `scanner.py` sử dụng chiến lược 3 lớp để đảm bảo phát hiện đa dạng các thiết bị mạng mà vẫn duy trì khả năng tránh spam/lag cho OS: `scanner.py` ng chiến lược 3 lớp, đảm bảo phát hiện đầy đủ mà không cần quyền Root:
1. **Ping Sweep:** Gửi gói tin Ping song song (tối đa 50 threads) để đánh thức thiết bị và điền IP/MAC vào bảng ARP Cache của hệ điều hành. | Giai đoạn | Mô tả |
2. **ARP Table Fallback:** Đọc bảng ARP nội bộ của OS (`arp -a`) bằng Regex. Hoạt động đa nền tảng (Windows/macOS/Linux) mà không cần quyền Admin/Root. | -------------- | ----------------------------------------------------------------------------------------------------------------------------- |
3. **Scapy ARP (Tính năng ng cao):** Gửi các gói tin ARP Broadcast trực tiếp để đảm bảo bao phủ gap nếu có. Yêu cầu quyền Root trên Linux/macOS hoặc Npcap trên Windows. | **Ping Sweep** | Gửi ping song song (100 threads) đến toàn bộ host trong dải `/24` để đánh thức thiết bị và điền ARP cache |
_Ngoài ra, công cụ tự động dò Hostname (`socket.gethostbyaddr`) song song để lấy tên thiết bị._ | **ARP Table** | Đọc `arp -a` bằng regex, hỗ trợ cả định dạng Windows (`cc-2d-...`) và macOS/Linux (`aa:bb:...`) |
| **Scapy ARP** | Gửi ARP broadcast trực tiếp để 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 |
### 2. Giao diện thiết bị (Device Table) Kết quả được merge theo IP và sort tăng dần trước khi trả về UI.
- Thiết bị được thu thập sẽ hiện ra trên UI dạng bảng, với tính năng tuỳ chọn hiển thị (checkbox ẩn Gateway mạng hiện tại và chính máy tính đang quét phần mềm). ### 2. Bảng thiết bị (Device Table)
- Trạng thái các luồng UI được tách rời bằng QThread + QPropertyAnimation cho hộp Collapsible nhằm tự động cuộn khi ẩn hiện nội dung, không làm treo ứng dụng.
### 3. Nạp Firmware (OpenWrt Flasher) - 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
Từ phiên bản hiện tại, phương thức ESP32 OTA không còn được áp dụng, thay vào đó module `flasher.py` tự động hóa quá trình update của router **OpenWrt (Barrier Breaker 14.07)**: ### 3. Flash Firmware
- **Bước 1:** Gửi `POST` chứa thông tin đăng nhập (username, password mặc định là root/trống, hoặc luci_username tuỳ thuộc firmware) để lấy session cookie `stok`. Có 2 chế độ và 2 method, tổng cộng 3 luồng thực thi khác nhau:
- **Bước 2:** Đẩy file firmware `*.bin` dạng `multipart/form-data` lên `/cgi-bin/luci/;stok=.../admin/system/flashops`.
- **Bước 3:** Gửi lệnh tiếp tục (Kèm tuỳ chọn giữ cấu hình `keep=on` nếu có). #### Chế độ `New Flash` — dùng cho thiết bị vừa reset cứng
- **Bước 4:** Thiết bị xác nhận và tự khởi động lại. Cập nhật Status trên Application.
- 🛠 Hỗ trợ `ThreadPoolExecutor` để Flash đồng thời nhiều thiết bị, với lựa chọn số luồng đồng thời tuỳ chỉnh. | 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. 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 ## ⚙️ Cấu hình mặc định
| Tham số | Mô tả | | Tham số | Giá trị mặc định | Mô tả |
| ---------------------- | ------------------------------------------------------------------- | | ---------------------- | ------------------------------------ | --------------------------------- |
| **Network** | Dải mạng quét (vd: `192.168.1.0/24`). Tự động tính từ local IP app. | | **Network** | Tự suy ra từ local IP (`x.x.x.0/24`) | Dải mạng để quét |
| **Show all** | Tuỳ chọn cho phép liệt kê cả Gateway IP máy chủ. | | **Flash Mode** | `New Flash` | Nạp mới hoặc Update |
| **Concurrent devices** | Số luồng để đĩa flash song song. `0` = Không giới hạn. | | **Method** | `API (LuCI)` | Phương thức flash cho New Flash |
| **Firmware filter** | Mặc định hiển thị và hỗ trợ `.bin`, `.hex`, `.uf2`. | | **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 |
--- ---
## 🔒 Lưu ý bảo mật & Quyền ## 🔒 Lưu ý bảo mật & Quyền
- **Quyền Scanner:** Ở chế độ quét Scapy (Sâu), cần khởi chạy bằng quyền Admin (Windows) hoặc Sudo (macOS/Linux), tuy nhiên App có thể chạy kể cả không có quyền Admin nhờ chiến lược Ping Sweep + `arp -a`. - **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`.
- **Ẩn Console (Window):** Khi gọi subproces (`ping`, `arp`), ứng dụng có tích hợp thuộc tính platform `CREATE_NO_WINDOW` để ngăn chặn các terminal đen giật nháy trên màn hình Windows. - **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.
- **Bảo mật mạng:** Quá trình tải firmware lên thiết bị thông qua HTTP thuần, chỉ nên sử dụng ở mạng LAN nội bộ, tránh những mạng mở hoặc public để tránh lộ file firmware. - **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ở.
- **LuCI Login:** API này nhắm thẳng vào quá trình đăng nhập qua form, nên sẽ tự động handle các router đang dùng OpenWrt Barrier Breaker mà không cần phải can thiệp tay. - **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.