diff --git a/read_dmesg.py b/read_dmesg.py index f296c59..208f443 100644 --- a/read_dmesg.py +++ b/read_dmesg.py @@ -30,9 +30,9 @@ gw.run_cmd("rm -f " + fn_remote) -with open(fn_local, "r") as file: +with open(fn_local, "r", encoding="utf-8", errors="replace") as file: data = file.read() -with open(fn_local, "w") as file: +with open(fn_local, "w", encoding="utf-8") as file: file.write(data) print("Kernel logs written to file {}".format(fn_local)) @@ -40,7 +40,7 @@ fn_old = 'outdir/syslog_old.txt' fn_local = 'outdir/syslog.txt' fn_remote = '/tmp/syslog.txt' -if os.path.exists(fn_local): +if os.path.exists(fn_local): if os.path.exists(fn_old): os.remove(fn_old) os.rename(fn_local, fn_old) @@ -50,9 +50,9 @@ gw.download(fn_remote, fn_local) gw.run_cmd("rm -f " + fn_remote) -with open(fn_local, "r") as file: +with open(fn_local, "r", encoding="utf-8", errors="replace") as file: data = file.read() -with open(fn_local, "w") as file: +with open(fn_local, "w", encoding="utf-8") as file: file.write(data) -print("System logs are written to a file {}".format(fn_local)) +print("System logs are written to a file {}".format(fn_local)) \ No newline at end of file diff --git a/reset_password.py b/reset_password.py new file mode 100644 index 0000000..f8c220f --- /dev/null +++ b/reset_password.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Script to reset a forgotten root password (SSH/TELNET). +Attempts to connect via SSH (if password is saved in config) or via TELNET, +then sets a new password. + +Usage: + ./run.sh reset_password.py [new_password] + ./run.sh reset_password.py --ip 192.168.31.1 new_password + +Requirements: + - The device must be on the same network + - Either SSH (with known password) OR TELNET (usually password "root") must be accessible +""" + +import sys +import time + +import xmir_base +import gateway +from gateway import die + + +def reset_password_via_gateway(gw, new_passw): + """Resets the password using an already connected gateway (SSH or TELNET).""" + gw.run_cmd('echo -e "{new_passw}\\n{new_passw}" | passwd root'.format(new_passw=new_passw)) + time.sleep(0.5) + if gw.use_ssh: + gw.ssh_close() + else: + gw.shutdown() + if gw.check_ssh(gw.ip_addr, gw.ssh_port, new_passw) != 0: + die('Failed to verify the new password via SSH') + gw.passw = new_passw + print("Root password successfully changed.") + + +def main(): + if len(sys.argv) > 1 and sys.argv[1] == '--ip': + if len(sys.argv) < 4: + die("Usage: reset_password.py --ip ") + ip_addr = sys.argv[2] + new_passw = sys.argv[3] + elif len(sys.argv) > 1: + new_passw = sys.argv[1] + ip_addr = None + else: + new_passw = input("Enter new password for root user: ") + ip_addr = None + + new_passw = new_passw.strip() + if len(new_passw) == 0: + die('Password cannot be empty!') + + # Create gateway without automatic SSH detection + gw = gateway.Gateway(detect_ssh=False) + + if ip_addr: + gw.ip_addr = ip_addr + print("Using IP: {}".format(ip_addr)) + + if gw.status < 1: + gw.detect_device() + if gw.status < 1: + die("Xiaomi device not found (IP: {})".format(gw.ip_addr)) + + print("Device: {}".format(gw.device_name)) + print("IP: {} SSH port: {}".format(gw.ip_addr, gw.ssh_port)) + + # 1. Try SSH with saved password (if present in memcfg) + if gw.passw: + ret = gw.check_ssh(gw.ip_addr, gw.ssh_port, gw.passw) + if ret >= 0: + print("SSH connection with saved password — OK") + gw.use_ssh = True + reset_password_via_gateway(gw, new_passw) + return + + # 2. Try TELNET (password is usually "root" after unlock) + if not gw.check_telnet(timeout=3): + die("Neither SSH (with known password) nor TELNET is accessible.\n" + "Ensure TELNET is enabled on the device (telnet_en=1).") + + print("TELNET detected. Attempting to connect...") + + telnet_passwords = ['root'] + if gw.passw and gw.passw != 'root': + telnet_passwords.insert(0, gw.passw) + if gw.xqpassword and gw.xqpassword not in telnet_passwords: + telnet_passwords.append(gw.xqpassword) + + tn = None + used_passw = None + for psw in telnet_passwords: + tn = gw.get_telnet(verbose=0, password=psw) + if tn: + used_passw = psw + break + + if not tn: + die("Failed to connect via TELNET.\n" + "Tried passwords: " + ", ".join(repr(p) for p in telnet_passwords) + + "\nTry the default password: root") + + print("TELNET connection — OK (password: {})".format(repr(used_passw))) + gw.use_ssh = False + gw.passw = used_passw + gw.ping(verbose=0) + + reset_password_via_gateway(gw, new_passw) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..fab1630 --- /dev/null +++ b/test.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +XMiR Patcher environment diagnostic test. +Checks imports, configuration, and device availability. +""" + +import sys + +def main(): + print("=" * 50) + print("XMiR Patcher — Environment Diagnostic") + print("=" * 50) + + # 1. Import tests + print("\n[1] Imports...") + try: + import xmir_base + print(" xmir_base — OK") + except Exception as e: + print(" xmir_base — ERROR:", e) + return 1 + + try: + import gateway + print(" gateway — OK") + except Exception as e: + print(" gateway — ERROR:", e) + return 1 + + try: + import xqmodel + print(" xqmodel — OK") + except Exception as e: + print(" xqmodel — ERROR:", e) + return 1 + + try: + import read_info + print(" read_info — OK") + except Exception as e: + print(" read_info — ERROR:", e) + + # 2. Configuration + print("\n[2] Configuration...") + try: + gw = gateway.Gateway(detect_device=False, detect_ssh=False) + print(" Device IP: {}".format(gw.ip_addr)) + print(" SSH port: {}".format(gw.ssh_port)) + if gw.passw: + print(" Password (memcfg): saved") + else: + print(" Password (memcfg): not set") + except PermissionError as e: + print(" Shared memory error (possibly sandboxed): {}".format(e)) + gw = None + except Exception as e: + print(" Initialization error: {}".format(e)) + gw = None + + # 3. Device detection (without SSH) + print("\n[3] Device detection...") + if gw: + try: + gw.detect_device() + if gw.status >= 1: + print(" Device found: {}".format(gw.device_name)) + print(" ROM: {} {}".format(gw.rom_version or "?", gw.rom_channel or "")) + else: + print(" Device not found (IP: {})".format(gw.ip_addr)) + except SystemExit: + print(" Device unavailable or requires initial setup via WEB UI") + except Exception as e: + print(" Error: {}".format(e)) + else: + print(" Skipped (Gateway not initialized)") + + # 4. Supported models + print("\n[4] Known models (examples): R3G, R3P, RM2100, R4A, WR30...") + print(" Total in xqmodel: {} models".format(len(xqmodel.xqModelList))) + + # 5. Unsupported models + print("\n[5] Unsupported features:") + print(" BE3600, BE2600 2.5G and other Wi-Fi 7 (BE-series) routers:") + print(" — Reason: use Qualcomm IPQ (ARM), not MediaTek MT7621 (MIPS)") + print(" — Breed bootloader and certain features are designed only for MIPS") + print(" — Supported models: R3G, R3P, RM2100") + + print("\n" + "=" * 50) + print("Diagnostic completed.") + print("=" * 50) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file