From c9a4a6d8cda52c2a1a24c442952460544bd9c70d Mon Sep 17 00:00:00 2001 From: remittor Date: Sun, 7 Nov 2021 12:47:53 +0300 Subject: [PATCH] Add support new Xiaomi devices (r3600 and rb03) --- connect.py | 12 ++-- connect3.py | 117 +++++++++++++++++++++++++++++++++++ xqimage.py | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 297 insertions(+), 7 deletions(-) create mode 100644 connect3.py create mode 100644 xqimage.py diff --git a/connect.py b/connect.py index 1b7c9d3..3a39fe0 100644 --- a/connect.py +++ b/connect.py @@ -13,7 +13,7 @@ import tarfile sys.path.append(os.path.dirname(os.path.abspath(__file__))) import gateway -from gateway import die +from gateway import * gw = gateway.Gateway(detect_device = False) @@ -26,17 +26,15 @@ else: gw.ip_addr(ip_addr) gw.save_config() -def get_http_headers(): - headers = {} - headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8" - headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0" - return headers - gw = gateway.Gateway(timeout = 4) if gw.status < 1: die("Xiaomi Mi Wi-Fi device not found (IP: {})".format(ip_addr)) dname = gw.device_name +if dname == 'r3600' or dname == 'rb03': + import connect3 + sys.exit(0) + print("device_name =", gw.device_name) print("rom_version = {} {}".format(gw.rom_version, gw.rom_channel)) print("mac = {}".format(gw.mac_address)) diff --git a/connect3.py b/connect3.py new file mode 100644 index 0000000..6e99089 --- /dev/null +++ b/connect3.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import sys +import types +import platform +import ctypes +import binascii +import re + +sys.path.append(os.path.dirname(os.path.abspath(__file__))) +from xqimage import * +from gateway import * +from read_info import * +from envbuffer import * + + +def i2b(value): + return value.to_bytes(4, byteorder='little') + +def build_sign(model, ver): + model = model.upper() + payload = None + if model == "R3G": # TEST + poffset = 0x1070 + payload = b'\x66' * 16 # TEST + if model == "RA69": + poffset = 0x1070 + payload = i2b(0x4152A8) + i2b(0) + i2b(0x402634) + i2b(0) + if model == "RA70": + poffset = 0x1078 + payload = i2b(0x4152D0) + i2b(0) + i2b(0x40265C) + i2b(0) + if model == "RA72": + poffset = 0x1078 + payload = i2b(0x4152E0) + i2b(0) + i2b(0x402630) + i2b(0) + if model == "R3600": + poffset = 0x1070 + payload = i2b(0x415290) + i2b(0) + i2b(0x402634) + i2b(0) + if model == "RB03": + poffset = 0x1078 + payload = i2b(0x4148B0) + i2b(0) + i2b(0x40263C) + i2b(0) + if not payload: + die('Payload is not defined for device "{}".'.format(model)) + # add header of sign section (16 bytes) + sign = (poffset + len(payload)).to_bytes(4, byteorder='little') + b'\x00' * 12 + # add fake sign + sign += b'\xEA' * poffset + # add payload + sign += payload + return sign + + +gw = Gateway(timeout = 4) +if gw.status < 1: + die("Xiaomi Mi Wi-Fi device not found (IP: {})".format(gw.ip_addr)) + +print("device_name =", gw.device_name) +print("rom_version = {} {}".format(gw.rom_version, gw.rom_channel)) +print("MAC Address = {}".format(gw.mac_address)) + +signature = build_sign(gw.device_name, gw.rom_version) + +dinfo = DevInfo(gw, verbose = 0, infolevel = 0) +syslog = SysLog(gw, timeout = 10, verbose = 1, infolevel = 2) + +syslog.verbose = 2 +bdata = syslog.parse_bdata() +bdata.var['boot_wait'] = "on" +bdata.var['uart_en'] = "1" +bdata.var['telnet_en'] = "1" +bdata.var['ssh_en'] = "1" +bdata.var['CountryCode'] = "EU" +partname = 'bdata' +bdata.mtd = syslog.get_mtd_by_name(partname) +if not bdata.mtd: + die('MTD partition "{}" not found!'.format(partname)) +#bdata_env_size = 0x4000 +bdata_env_size = 0x10000 # fixed size of BData environment buffer +bdata.buf = bdata.pack(bdata_env_size) +bdata.buf += b'\xFF' * (bdata.mtd.size - len(bdata.buf)) +bdata.img = XQImage(gw.device_name) +bdata.img.add_version(gw.rom_version) +bdata.img.add_file(bdata.buf, 'bdata.bin', mtd = bdata.mtd.id) + +partname = 'crash' +crash_mtd = syslog.get_mtd_by_name(partname) +if not crash_mtd: + die('MTD partition "{}" not found!'.format(partname)) + +def create_crash_image(mtd, prefix, outfilename): + crash = types.SimpleNamespace() + crash.mtd = mtd + if prefix is None: + prefix = b'' + crash.buf = bytearray(prefix + b'\xFF' * (mtd.size - len(prefix))) + crash.img = XQImage(gw.device_name) + crash.img.add_version(gw.rom_version) + crash.img.add_file(crash.buf, 'crash.bin', mtd = mtd.id) + crash.img.save_image(signature, outfilename) + print('Created hacked image file: "{}"'.format(outfilename)) + return crash + +# image for activate "factory mode" via uboot (insert factory_mode=1 into kernel cmdline) +fn_crash1 = 'outdir/image_{device}_1_crash.bin'.format(device = gw.device_name) +crash1 = create_crash_image(crash_mtd, b'\xA5\x5A\x00\x00', fn_crash1) + +# image for change BData environment +bdata.img.save_image(signature, 'outdir/image_{device}_2_bdata.bin'.format(device = gw.device_name)) +print('Created hacked image file: "{}"'.format(bdata.img.outfilename)) + +# image for deactivate "factory mode" via uboot +fn_crash3 = 'outdir/image_{device}_3_crash.bin'.format(device = gw.device_name) +crash3 = create_crash_image(crash_mtd, None, fn_crash3) + +print("OK_finish") + diff --git a/xqimage.py b/xqimage.py new file mode 100644 index 0000000..2951d8d --- /dev/null +++ b/xqimage.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import sys +import types +import platform +import ctypes +import binascii + + +class XQImgHdr(ctypes.Structure): + _fields_ = [("magic", ctypes.c_uint), # HDR1 + ("sign", ctypes.c_uint), # offset of sign block + ("crc32", ctypes.c_uint), # crc32 check sum + ("type", ctypes.c_ushort), # ROM type (12 = miwifi_ssh.bin) + ("model", ctypes.c_short), # device number + ("files", ctypes.c_uint * 8)] # array of section-offset + +class XQImgFile(ctypes.Structure): + _fields_ = [("magic", ctypes.c_ushort), # BE BA + ("rsvd0", ctypes.c_ushort), + ("addr", ctypes.c_uint), # Flash Address + ("size", ctypes.c_uint), # size of file + ("mtd", ctypes.c_short), # mtd number for flashing + ("dummy", ctypes.c_short), + ("name", ctypes.c_char * 32)] # Filename + +xqModelList = [ + "", + "", + "", + "R1CM", # 3 + "R2D", # 4 + "R1CL", + "R2CM", + "R3", + "R3D", # 8 + "R3L", + "R3P", # 10 + "P01", + "R3A", + "R3G", # 13 + "R4", + "R4C", + "D01", + "R4A", + "R4CM", + "R4AC", + "R3GV2", # 20 + "R2600", + "R2100", # 22 + "RM2100", # 23 + "R3600", # 24 + "R1350", + "R2200", + "R2350", # 27 + "IR1200G", + "R1800", + "R2100D", # 30 + "RA67", + "RA69", + "RA71", + "CR6006", # 34 + "CR6008", + "CR6009", + "RA70", + "RA75", + "RA72", + "", # 40 + "", + "", + "RA80", + "RA81", + "RA82", + "RA83", + "RA74", + "", + "YY01", + "RB01", # 50 + "RB03" # 51 +] + +def get_modelid_by_name(name): + for i, m in enumerate(xqModelList): + if m.lower() == name.lower(): + return i + return -1 + + +class XQImage(): + model = None + type = 0 + header = XQImgHdr() + version = None + files = [] # list of files + + def __init__(self, model = None, type = 0): + self.model = None + self.type = 0 + self.header = XQImgHdr() + self.version = None + self.files = [] + if model is None: + self.model = None + else: + if isinstance(model, int): + self.model = model + else: + self.model = get_modelid_by_name(model) + self.type = type + + def add_version(self, version, hardware = 0, channel = 'release'): + self.version = None + if version is None: + return + data = "config core 'version'\n" + data += "\t" + "option ROM '{}'\n".format(version) + if channel: + data += "\t" + "option CHANNEL '{}'\n".format(channel.lower()) + if hardware is not None: + if isinstance(hardware, int): + if hardware == 0 and self.model: + hardware = xqModelList[self.model] + else: + hardware = xqModelList[hardware] + data += "\t" + "option HARDWARE '{}'\n".format(hardware.upper()) + self.version = data.encode('ascii') + if len(self.version) & 3 != 0: + self.version += b'\x00' * (4 - len(self.version) & 3) + self.add_file(self.version, 'xiaoqiang_version') + + def add_file(self, data, name, mtd = None): + file = types.SimpleNamespace() + file.data = data + file.header = XQImgFile() + file.header.magic = int.from_bytes(b'\xBE\xBA', byteorder='little') + file.header.rsvd0 = 0 + file.header.addr = 0xFFFFFFFF + file.header.size = len(data) + file.header.mtd = 0xFFFF if mtd is None else mtd + file.header.dummy = 0 + file.header.name = name.encode('ascii') + self.files.append(file) + + def get_image(self, sign): + buf = bytearray() + self.header = XQImgHdr() + self.header.magic = int.from_bytes(b'HDR1', byteorder='little') + self.header.sign = 0 + self.header.crc32 = 0 + self.header.type = self.type + self.header.model = self.model + buf += bytes(self.header) + for i, f in enumerate(self.files): + self.header.files[i] = len(buf) + buf += bytes(f.header) + buf += f.data + self.header.sign = len(buf) + buf += sign + for i in range(ctypes.sizeof(self.header)): + buf[i] = bytes(self.header)[i] + self.header.crc32 = 0xFFFFFFFF - binascii.crc32(buf[12:]) # JAMCRC + for i in range(12): + buf[i] = bytes(self.header)[i] + return buf + + def save_image(self, sign, filename): + self.outfilename = filename + buf = self.get_image(sign) + with open(filename, 'wb') as file: + file.write(buf) + + +