Pull to refresh

Модификация прошивки роутера D-Link

Reading time17 min
Views17K

Всех с наступившим Рождеством! В этой заметке я расскажу о том как модифицировать прошивку роутера D-Link DWR-M921, вдруг кому эта информация пригодится.

Привели меня к этому попытки установить на роутер блокировщик рекламы AdGuardHome. Чипсет RTL8197F на котором работает сабж на данный момент не имеет полной поддержки OpenWRT, исходных кодов прошивки от D-Link'а нет, а попытки что-то собрать из древних RealTek'овских SDK для семейства rtl819x которые я нашел на SourceForge не увенчались успехом.

К счастью, на плате нашелся UART. Бутлог под спойлером:

Бутлог

Booting...
init_ram
M init ddr ok

DRAM Type: DDR2
DRAM frequency: 400MHz
DRAM Size: 128MB
JEDEC id EF4017, EXT id 0x0000
found w25q64
flash vendor: Winbond
w25q64, size=8MB, erasesize=4KB, max_speed_hz=29000000Hz
auto_mode=0 addr_width=3 erase_opcode=0x00000020
=>CPU Wake-up interrupt happen! GISR=89000004

---Realtek RTL8197F boot code at 2020.11.02-17:16+0800 v3.4T-pre2.1 (999MHz)
bootbank is 1, bankmark 80000006
Jump to image start=0x80a00000...
return_addr = b0030000 ,boot bank=1, bank_mark=0x80000006...
decompressing kernel:
Uncompressing Linux... done, booting the kernel.
done decompressing kernel.
start address: 0x8051f440
Linux version 3.10.90 (jenkins@zhao-MS-7B23) (gcc version 4.4.7 (Realtek MSDK-4.4.7 Build 2001) ) #12 Mon Aug 9 10:52:50 CST 2021
bootconsole [early0] enabled
CPU revision is: 00019385 (MIPS 24Kc)
Determined physical RAM map:
memory: 08000000 @ 00000000 (usable)
Zone ranges:
Normal [mem 0e start for each node
Early memory node ranges
node 0: [mem 0x00000000-0x07ffffff]
Primary instruction cache 64kB, VIPT, 4-way, linesize 32 bytes.
Primary data cache 32kB, 4-way, PIPT, no aliases, linesize 32 bytes
Built 1 zonelists in Zone order, mobility grouping on. Total pages: 8176
Kernel command line: console=ttyS0,38400 root=/dev/mtdblock1
PID hash table entrix00000000-0x07ffffff]
Movable zones: 512 (order: -3, 2048 bytes)
Dentry cache hash table entries: 16384 (order: 2, 65536 bytes)
Inode-cache hash table entries: 8192 (order: 1, 32768 bytes)
Writing ErrCtl register=00002bcb
Readback ErrCtl register=00002bcb
Memory: 114544k/131072k available (5267k kernel code, 16528k reserved, 1733k data, 224k init, 0k highmem)
SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
NR_IRQS:192
Realtek GPIO IRQ init
Calibrating delay loop... 660.68 BogoMIPS (lpj=3303424)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 2048
devtmpfs: initialized
NET: Registered protocol family 16
<<<<>>>>
Do MDIO_RESET
40MHz
PCIE -> Cannot LinkUP
INFO: initializing USB devices ...
enable port 0 two port enable
port 0 org 0xe0=e2
port 0 org 0xe1=31
port 0 org 0xe2=39
port 0 org 0xe4=98
port 0 org 0xe6=c0
port 1 org 0xe0=e2
port 1 org 0xe1=31
port 1 org 0xe2=39
port 1 org 0xe4=98
port 1 org 0xe6=c0
patch new usb phy para for 40M OSC
system reg b8000010=0x80043000
system reg b8000014=0x118
system reg b8000160=0x1
system reg b8000164=0x0
system reg b8000168=0x0
system reg b800016c=0x280500
system reg b8000180=0x60000
system reg b8021094=0x200020
system reg b8140200=0xf002cc11
system reg b8140204=0x0
system reg b8140208=0x2010000
system reg b814020c=0x9b00
system reg b8140210=0xca00ca

EHCI reg b8021050=0x0
EHCI reg b8021054=0x2000
EHCI reg b8021058=0x2000
OHCI reg b8020000=0x110
OHCI reg b8020004=0x0
port 0 reg e0=e2
port 0 reg e1=31
port 0 reg e2=33
port 0 reg e3=8d
port 0 reg e4=c9
port 0 reg e5=19
port 0 reg e6=c1
port 0 reg e7=91
port 0 reg f0=fc
port 0 reg f1=8c
port 0 reg f2=0
port 0 reg f3=11
port 0 reg f4=9b
port 0 reg f5=4
port 0 reg f6=0
port 1 reg e0=e2
port 1 reg e1=31
port 1 reg e2=33
port 1 reg e3=8d
port 1 reg e4=c9
port 1 reg e5=19
port 1 reg e6=c1
port 1 reg e7=91
port 1 reg f0=fc
port 1 reg f1=8c
port 1 reg f2=0
port 1 reg f3=11
port 1 reg f4=9b
port 1 reg f5=4
port 1 reg f6=0
Realtek GPIO controller driver init
INFO: registering sheipa spi device
bio: create slab at 0
SCSI subsystem initialized
INFO: sheipa spi driver register
INFO: sheipa spi probe
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
Switching to clocksource MIPS
NET: Registered protocol family 2
TCP established hash table entries: 2048 (order: 0, 16384 bytes)
TCP bind hash table entries: 2048 (order: -1, 8192 bytes)
TCP: Hash tables configured (established 2048 bind 2048)
TCP: reno registered
UDP hash table entries: 1024 (order: 0, 16384 bytes)
UDP-Lite hash table entries: 1024 (order: 0, 16384 bytes)
NET: Registered protocol family 1
squashfs: version 4.0 (2009/01/31) Phillip Lougher
exFAT: Version 1.2.9
NTFS driver 2.1.30 [Flags: R/W].
fuse init (API version 7.22)
msgmni has been set to 223
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 254)
io scheduler noop registered (default)
Serial: 8250/16550 driver, 1 ports, IRQ sharing disabled
console [ttyS0] enabled, bootconsole disabled7) is a 16550A
console [ttyS0] enabled, bootconsole disabled
Realtek GPIO Driver for Flash Reload Default
loop: module loaded
m25p80 spi0.0: change speed to 15000000Hz, div 7
JEDEC id EF4017
m25p80 spi0.0: found w25q64, expected m25p80
flash vendor: Winbond
m25p80 spi0.0: w25q64 (8192 Kbytes) (29000000 Hz)
4 rtkxxpart partitions found on MTD device m25p80
Creating 4 MTD partitions on "m25p80":
0x000000000000-0x000000300000 : "boot+cfg+linux"
0x000000300000-0x000000800000 : "rootfs"
0x000000800000-0x000000b00000 : "boot+cfg+linux2"
mtd: partition "boot+cfg+linux2" is out of reach -- disabled
0x000000b00000-0x000001000000 : "rootfs2"
mtd: partition "rootfs2" is out of reach -- disabled
PPP generic driver version 2.4.2
PPP BSD Compression module registered
PPP Deflate Compression module registered
NET: Registered protocol family 24
MPPE/MPPC encryption/compression module registered
Realtek WLAN driver - version 1.7 (2015-10-30)(SVN:9914)
Adaptivity function - version 9.3.4
MACHAL_version_init
RFE TYPE =0
##########(wlan0)##########
SKB_BUF_SIZE[2472] MAX_SKB_NUM[1500]
NUM_RX_DESC[512] NUM_TX_DESC[512]
MAX_RX_BUF_LEN[2040]
############################
RFE TYPE =0
RFE TYPE =0
RFE TYPE =0
RFE TYPE =0
RFE TYPE =0
usbcore: registered new interface driver asix
usbcore: registered new interface driver ax88179_178a
usbcore: registered new interface driver cdc_ether
usbcore: registered new interface driver net1080
usbcore: registered new interface driver rndis_host
usbcore: registered new interface driver cdc_subset
usbcore: registered new interface driver zaurus
usbcore: registered new interface driver cdc_ncm
GobiNet: Quectel_Linux_GobiNet_SR01A02V16
usbcore: registered new interface driver GobiNet
ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
rtl819x-ehci rtl819x-ehci: Realtek rtl819x On-Chip EHCI Host Controller
rtl819x-ehci rtl819x-ehci: new USB bus registered, assigned bus number 1
rtl819x-ehci rtl819x-ehci: irq 21, io mem 0x18021000
rtl819x-ehci rtl819x-ehci: USB 2.0 started, EHCI 1.00
usb usb1: New USB device found, idVendor=1d6b, idProduct=0002
usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
usb usb1: Product: Realtek rtl819x On-Chip EHCI Host Controller
usb usb1: Manufacturer: Linux 3.10.90 ehci_hcd
usb usb1: SerialNumber: rtl819x-ehci
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 2 ports detected
ehci-platform: EHCI generic platform driver
ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
rtl819x-ohci rtl819x-ohci: Realtek rtl819x built-in OHCI controller
rtl819x-ohci rtl819x-ohci: new USB bus registered, assigned bus number 2
rtl819x-ohci rtl819x-ohci: irq 21, io mem 0x18020000
usb usb2: New USB device found, idVendor=1d6b, idProduct=0001
usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1
usb usb2: Product: Realtek rtl819x built-in OHCI controller
usb usb2: Manufacturer: Linux 3.10.90 ohci_hcd
usb usb2: SerialNumber: rtl819x-ohci
hub 2-0:1.0: USB hub found
hub 2-0:1.0: 2 ports detected
uhci_hcd: USB Universal Host Controller Interface driver
usbcore: registered new interface driver cdc_acm
cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
usbcore: registered new interface driver cdc_wdm
usbcore: registered new interface driver usb-storage
usbcore: registered new interface driver usbserial
usbcore: registered new interface driver usbserial_generic
usbserial: USB Serial support registered for generic
usbcore: registered new interface driver option
usbserial: USB Serial support registered for GSM modem (1-port)
usbcore: registered new interface driver GobiSerial
usbserial: USB Serial support registered for GobiSerial
GobiSerial: 2015-08-27/SWI_2.25:GobiSerial
Otg act as Host mode
=>do UTMI reset, r=1
=>do UTMI reset, r=0
RTL8197F u2 phy 40MHz patch
reg e0=e2
reg e1=31
reg e2=33
reg e3=8d
reg e4=c9
reg e5=19
reg e6=c1
reg e7=91
reg f0=fc
reg f1=8c
reg f2=0
reg f3=11
reg f4=9b
reg e0=25
reg e1=4f
reg e2=0
reg e3=0
reg e4=0
reg e5=a
reg e6=0
reg e7=0
reg f0=fc
reg f1=8c
reg f2=0
reg f3=11
reg f4=bb
=>do UTMI reset, r=1
=>do UTMI reset, r=0
create lmdev=87262f00
device_register :register pass
dwc_otg: version 3.10b 20-MAY-2013
dwc_otg_driver_probe(87262f00)
start=0xb8030000
base=0xb8030000
dwc_otg_device=0x87347b00
Core Release: 3.10a
Setting default values for core params
=> SNPSID=4f54310a
Using Buffer DMA mode
Periodic Transfer Interrupt Enhancement - disabled
Multiprocessor Interrupt Enhancement - disabled
OTG VER PARAM: 0, OTG VER FLAG: 0
Shared Tx FIFO mode
dwc_otg logicmodule: DWC OTG Controller
dwc_otg logicmodule: new USB bus registered, assigned bus number 3
dwc_otg logicmodule: irq 20, io mem 0xb8030000
Init: Power Port (0)
usb usb3: New USB device found, idVendor=1d6b, idProduct=0002
usb usb3: New USB device strings: Mfr=3, Product=2, SerialNumber=1
usb usb3: Product: DWC OTG Controller
usb usb3: Manufacturer: Linux 3.10.90 dwc_otg_hcd
usb usb3: SerialNumber: logicmodule
hub 3-0:1.0: USB hub found
hub 3-0:1.0: 1 port detected
u32 classifier
nf_conntrack version 0.5.0 (1789 buckets, 7156 max)
gre: GRE over IPv4 demultiplexor driver
ip_gre: GRE over IPv4 tunneling driver
ip_tables: (C) 2000-2006 Netfilter Core Team
TCP: cubic registered
NET: Registered protocol family 10
ip6_tables: (C) 2000-2006 Netfilter Core Team
sit: IPv6 over IPv4 tunneling driver
NET: Registered protocol family 17
Bridge firewalling registered
Ebtables v2.0 registered
l2tp_core: L2TP core driver, V2.0
l2tp_ip: L2TP IP encapsulation support (L2TPv3)
l2tp_netlink: L2TP netlink interface
l2tp_eth: L2TP ethernet pseudowire support (L2TPv3)
8021q: 802.1Q VLAN Support v1.8
Realtek FastPath:v1.03

Probing RTL819X NIC-kenel stack size order[0]...
eth0 added. vid=9 Member port 0x1...
eth1 added. vid=8 Member port 0x10...
eth2 added. vid=9 Member port 0x2...
eth3 added. vid=9 Member port 0x4...
eth4 added. vid=9 Member port 0x8...
usb 1-1: new high-speed USB device number 2 using rtl819x-ehci
[peth0] added, mapping to [eth1]...
m25p80 spi0.0: change speed to 29000000Hz, div 4
VFS: Mounted root (squashfs filesystem) readonly on device 31:1.
devtmpfs: mounted
Freeing unused kernel memory: 224K (806d8000 - 80710000)
usb 1-1: New USB device found, idVendor=1508, idProduct=1001
usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-1: Product: Fibocom NL668 Modem
usb 1-1: Manufacturer: Fibocom NL668 Modem
usb 1-1: SerialNumber: 5735bff3
option 1-1:1.0: GSM modem (1-port) converter detected
usb 1-1: GSM modem (1-port) converter now attached to ttyUSB0
option 1-1:1.1: GSM modem (1-port) converter detected
usb 1-1: GSM modem (1-port) converter now attached to ttyUSB1
option 1-1:1.2: GSM modem (1-port) converter detected
usb 1-1: GSM modem (1-port) converter now attached to ttyUSB2
option 1-1:1.3: GSM modem (1-port) converter detected
usb 1-1: GSM modem (1-port) converter now attached to ttyUSB3
GobiNet 1-1:1.4 usb0: register 'GobiNet' at usb-rtl819x-ehci-1, GobiNet Ethernet Device, e6:ac:30:6f:25:01
init started: BusyBox v1.13.4 (2021-08-09 10:47:12 CST)
creating qcqmi0
Discovery the interface for Fibocom.cp: cannot stat '/etc/avahi-daemon.conf': No such file or directory
Floating point exception
Please refine linux/.config change offset to fit flash erease size!!!!!!!!!!!!!!!!!
type:3, enable:1, percent1


sysconf init gw all


Init Start...


sysconf wlanapp kill wlan0


open /proc/br_wlanblock: Permission denied
Init bridge interface...
device eth0 entered promiscuous mode
device eth2 entered promiscuous mode
device eth3 entered promiscuous mode
device eth4 entered promiscuous mode
device wlan0 entered promiscuous mode
WlanSupportAbility = 0x3
[ODM_software_init]
[97F] Bonding Type 97FN, PKG2
[97F] RFE type 0 PHY paratemters: DEFAULT
clock 40MHz
load efuse ok
rom_progress: 0x200006f
rom_progress: 0x400006f
[GetHwReg88XX][PHY_REG_PG_8197Fmp_Type0] size
[GetHwReg88XX][PHY_REG_PG_8197Fmp_Type0]
[GetHwReg88XX][rtl8197Ffw]
[GetHwReg88XX][rtl8197Ffw size]
[97F] Default BB Swing=30
br0: port 5(wlan0) entered forwarding state
br0: port 5(wlan0) entered forwarding state
br0: port 4(eth4) entered forwarding state
br0: port 4(eth4) entered forwarding state
br0: port 3(eth3) entered forwarding state
br0: port 3(eth3) entered forwarding state
br0: port 2(eth2) entered forwarding state
br0: port 2(eth2) entered forwarding state
br0: port 1(eth0) entered forwarding state
br0: port 1(eth0) entered forwarding state
killall: udhcpd: no process killed
Init Wlan application...

WiFi Simple Config v2.20-wps2.0 (2017.10.20-07:08+0000).

Register to wlan0
iwcontrol RegisterPID to (wlan0)
route: SIOCDELRT: No such process
IEEE 802.11f (IAPP) using interface br0 (v1.8)
+++set_wanipv6+++2028
Start setting IPv6[IPv6]
open /proc/sys/net/ipv4/rt_cache_rebuild_count: No such file or directory
/bin/sh: addgroup: not found
passwd: unknown user tmptest
rand min=48,sec=8!
/bin/sh: addgroup: not found
Changing password for admin
New password:
Bad password: too weak
Retype password:
passwd: password for admin is unchanged
adduser: login 'admin' is in use
iptables: Bad rule (does a matching rule exist in that chain?).
iptables: Bad rule (does a matching rule exist in that chain?).
-----update_users_traffic, 667, 192, 168, 0, 1
killall: ntpd: no process killed
/etc/init.d/rcS: line 123: can't create /proc/irq/33/smp_affinity: nonexistent directory
enable 0 interval


sysconf stopWispWan


boa: server version Boa/0.94.14rc21
boa: server built Aug 9 2021 at 10:47:07.
boa: starting server pid=1623, port 80
type:3, enable:0, percent0
Startup Ok

В веб-интерфейсе я нашел опцию включения telnet'а, но пароль от root-пользователя нигде не обнаружил. Написал в тех-поддержку и мне его прислали.

> telnet 192.168.0.1
Trying 192.168.0.1...
Connected to 192.168.0.1.
Escape character is '^]'.

DWR-M921 login: root
Password: 
RLX Linux version 2.0
         _           _  _
        | |         | ||_|                 
   _  _ | | _  _    | | _ ____  _   _  _  _ 
  | |/ || |\ \/ /   | || |  _ \| | | |\ \/ /
  | |_/ | |/    \   | || | | | | |_| |/    \
  |_|   |_|\_/\_/   |_||_|_| |_|\____|\_/\_/

For further information check:
http://processor.realtek.com/
#

Но, увы, доступа к терминалу оказалось недостаточно. Всё дело в том что в роутере используется сжатая файловая система только для чтения SquashFS, которая при запуске распаковывается в оперативную память, а значит все изменения внесённые во время работы роутера пропадают после перезагрузки.

# mount
rootfs on / type rootfs (rw)
/dev/root on / type squashfs (ro,relatime)
devtmpfs on /dev type devtmpfs (rw,relatime,mode=0755)
proc on /proc type proc (rw,relatime)
ramfs on /var type ramfs (rw,relatime)
sysfs on /sys type sysfs (rw,relatime)
debugfs on /sys/kernel/debug type debugfs (rw,relatime)
# 

Стало понятно что для того чтобы что-то сделать придётся редактировать прошивку.

Ну что ж, приступим.

Скачиваем последнюю версию с официального сайта и сканируем binwalk'ом:

> cp /home/gleb/Downloads/DWR_M921_fw20211118140944_S10865_V1.1.52\(1119134055\)\ \(1\).bin ./firmware.bin

> binwalk firmware.bin 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
10264         0x2818          LZMA compressed data, properties: 0x5D, dictionary size: 8388608 bytes, uncompressed size: 7374556 bytes
2394146       0x248822        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 5012602 bytes, 961 inodes, blocksize: 131072 bytes, created: 2038-04-24 04:11:44

Binwalk распознал два файла, а именно linux-ядро сжатое алгоритмом lzma и файловую систему squashfs (созданную, кстати, в 2038 году). Нас интересует файловая система. Извлекаем ее из файла прошивки утилитой dd и распаковываем полученный файл командой unsquashfs:

> dd bs=1 skip=2394146 if=firmware.bin of=squashfs.sfs
5013506+0 записей получено
5013506+0 записей отправлено
5013506 байт (5,0 MB, 4,8 MiB) скопирован, 21,4423 s, 234 kB/s

> sudo unsquashfs squashfs.sfs 
Parallel unsquashfs: Using 4 processors
920 inodes (984 blocks) to write

[======================================================================================================================================================================|] 984/984 100%

created 453 files
created 41 directories
created 118 symlinks
created 349 devices
created 0 fifos
created 0 sockets

> ls
firmware.bin  squashfs-root  squashfs.sfs

> ls ./squashfs-root/
bin  dev  etc  home  init  lib  mnt  proc  root  sys  tmp  usr  var  web

Теперь мы имеем директорию squashfs-root с содержимым корневой файловой системы роутера, которое мы можем изучать и редактировать. Для эксперимента я решил сменить пароль root пользователя на роутере. Из следующей строки файла /etc/init.d/rcS нам известно что при запуске роутер создает файл /var/passwd из файла /etc/passwd_orig:

cp /etc/passwd_orig /var/passwd

Меняем хэш оригинального пароля на новый. Во избежание возни с хэшами (в которых я мало разбираюсь) я просто сменил пароль в работающем роутере командой passwd и скопировал новый хэш из файла /etc/passwd.

На роутере:

# passwd
Changing password for root
New password:
Bad password: too short
Retype password:
Password for root changed by root
# cat /var/passwd
root:$1$QiHO6bVZ$k8BN/N3odnO62hEutlykE1:0:0:root:/:/bin/sh
nobody:x:0:0:nobody:/:/dev/nulltmptest:x:1000:1000:Linux User,,,:/home/tmptest:/bin/sh
admin:x:1000:1000:Linux User,,,:/home/admin:/bin/sh
#

В ./squashfs-root:

> cat ./etc/passwd_orig
root:$1$QiHO6bVZ$k8BN/N3odnO62hEutlykE1:0:0:root:/:/bin/sh
nobody:x:0:0:nobody:/:/dev/null⏎

> sudo sed -i 's+$1$86ANwXE8$/oiI3ZYM4plYjUYzXGH3E/+$1$QiHO6bVZ$k8BN/N3odnO62hEutlykE1+g' ./etc/passwd_orig

Для первого раза этих изменений хватит. Теперь нам предстоит собрать прошивку обратно и добиться того чтобы роутер принял ее. Для начала сжимаем файловую систему. Делаем мы это с помощью утилиты mksquashfs. Из вывода binwalk-а мы помним, что файловая система в оригинальной прошивке была сжата алгоритмом xz и имела размер блока 131072 байта. Выполняем:

> sudo mksquashfs ./squashfs-root/ squashfs-mod.sfs -b 131072 -comp xz
Parallel mksquashfs: Using 4 processors
Creating 4.0 filesystem on squashfs-mod.sfs, block size 131072.
[======================================================================================================================================================================/] 517/517 100%

Exportable Squashfs 4.0 filesystem, xz compressed, data block size 131072
        compressed data, compressed metadata, compressed fragments,
        compressed xattrs, compressed ids
        duplicates are removed
Filesystem size 4898.31 Kbytes (4.78 Mbytes)
        30.50% of uncompressed filesystem size (16061.77 Kbytes)
Inode table size 6260 bytes (6.11 Kbytes)
        21.64% of uncompressed inode table size (28929 bytes)
Directory table size 7320 bytes (7.15 Kbytes)
        46.03% of uncompressed directory table size (15904 bytes)
Number of duplicate files found 21
Number of inodes 961
Number of files 453
Number of fragments 55
Number of symbolic links 118
Number of device nodes 349
Number of fifo nodes 0
Number of socket nodes 0
Number of directories 41
Number of ids (unique uids + gids) 3
Number of uids 2
        gnome-initial-setup (124)
        root (0)
Number of gids 1
        pulse (128)

> ls
firmware.bin  squashfs-mod.sfs  squashfs-root  squashfs.sfs

..и получаем готовый файлик с модифицированной файловой системой. Сравниваем его со старым и убеждаемся что мы все правильно сделали:

> binwalk squashfs.sfs

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Squashfs filesystem, little endian, version 4.0, compression:xz, size: 5012602 bytes, 961 inodes, blocksize: 131072 bytes, created: 2038-04-24 04:11:44

> binwalk squashfs-mod.sfs 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Squashfs filesystem, little endian, version 4.0, compression:xz, size: 5015866 bytes, 961 inodes, blocksize: 131072 bytes, created: 2023-01-01 14:20:39

Размер файловой системы изменился, но для нас это не имеет значения, так как в оригинальной прошивке после файловой системы полно свободного места:

Теперь нам нужно поместить измененную ф.с. в прошивку. Делаем это с помощью команды dd:

> cp firmware.bin firmware-mod.bin
> dd conv=notrunc bs=1 seek=2394146 if=squashfs-mod.sfs of=firmware-mod.bin

Казалось бы все готово, но нет. При попытке обновления через веб интерфейс, роутер (чего и следовало ожидать) выдает ошибку несоответствия контрольной суммы:

Я долго ломал голову над тем как же пересчитать чексумму (да, я пытался вставить чексумму которую выдал веб-интерфейс в конец прошивки, ничего не вышло). Толковой информации в интернете я не нашел. Наткнулся лишь на firmware-mod-kit, который способен проделать всё ранее описанное автоматически. К сожалению, пересчитать контрольные суммы он тоже не смог.

...
CRC update failed.

Firmware header not supported; firmware checksums may be incorrect. 
...

Решил изучить процесс сборки прошивки в раздобытых SDK, как оказалось — не зря. В "rtl819x-SDK-v3.4.9.3-full-package", в файле ./rtl819x/boards/rtl819xD/tools/mkimg я нашел кусок кода который навёл меня на верную мысль:

# run squash fs here 
if [ "$CONFIG_SQUASHFS_LZMA" = "y" ]; then
	rm -f squashfs-lzma.o
        #$MKSQUASHFS_LZMA $RAMFSDIR squashfs-lzma.o -be -always-use-fragments 
        #$MKSQUASHFS $RAMFSDIR squashfs-lzma.o -comp lzma -always-use-fragments 
        $MKSQUASHFS $RAMFSDIR squashfs-lzma.o -comp lzma -always-use-fragments -pf squashfs-pf-list.txt
        if [ "$USE_SAMBA" = 1 -o "$USE_4M" = 1 ]; then
		$CVIMG root squashfs-lzma.o root.bin 2D0000 $ROOTFS_OFFSET
	else
		$CVIMG root squashfs-lzma.o root.bin F0000 $ROOTFS_OFFSET
	fi
		IMGSIZE=`du -s squashfs-lzma.o |  cut -f1`
else
	rm -f squashfs.o
	#$MKSQUASHFS $RAMFSDIR squashfs.o -always-use-fragments
	$MKSQUASHFS $RAMFSDIR squashfs.o -always-use-fragments -pf squashfs-pf-list.txt
	$CVIMG root squashfs.o root.bin F0000 $ROOTFS_OFFSET
	IMGSIZE=`du -s squashfs.o |  cut -f1`
fi
ROOTSIZE=`du -s $RAMFSDIR | cut -f1`
IMGBYTES=`du -b root.bin | cut -f1`
echo "=============================================="
echo "Summary:"
echo "==>Squashfs disk size  = $ROOTSIZE	KBytes"
echo "==>Squashfs image size = $IMGSIZE	KBytes"

...и в свою очередь:

CVIMG=$USERS_DIR/boa/tools/cvimg

Из этого следует, что после сжатия, файловая система обрабатывается некой программой "cvimg", которая нашлась в скомпилированном виде в том же SDK:

> ./cvimg help
Version: 1.1
Usage: cvimg <option> input-filename output-filename start-addr burn-addr [signature]
<option>: root|linux|boot|all|vmlinux|vmlinuxhdr|signature
[signature]: user-specified signature (4 characters)

Но я так и не смог обработать ею файл squashfs-mod.sfs, так как не смог угадать правильное значение для "signature", а работать с каким попало программа отказывалась.

Однако, в исходном коде загрузчика из другого SDK (а именно rtk_openwrtSDK_v2.5_20160905) я нашел другую версию cvimg, а в файле /bootcode/src/bootcode_rtl8197f/btcode/rom_def.h нашел следующую полезную информацию:

typedef struct _IMG_HEADER_TYPE_
{
	unsigned char signature[SIG_LEN];
	unsigned long startAddr;
	unsigned long burnAddr;
	unsigned long len;
} IMG_HEADER_TYPE, *PIMG_HEADER_TYPE;

...которая указывает нам на структуру заголовка прошивки. Как ни странно, эти переменные (а именно startAddr, burnAddr и signature) совпадают с данными которые от нас требует программа cvimg. Значит мы на верном пути. Вот так выглядит cvimg из этого SDK:

> ./cvimg
Usage: cvimg [root|linux|boot|all] input-filename output-filename start-addr burn-addr

Даём программе нашу отредактированную файловую систему, задаём случайные параметры и смотрим что она будет делать:

> sudo ./cvimg root squashfs-mod.sfs squashfs-mod.bin 11111111 22222222
Generate image successfully, length=5017602, checksum=0x93f2

Размер полученного файла squashfs-mod.bin на 18 байт больше исходного. Открываем оба файла в Ghex:

Сравнение squashfs-mod.sfs и squashfs-mod.bin
Сравнение squashfs-mod.sfs и squashfs-mod.bin

И так, видим что данная программа добавила в начало файла 16 байт с заданной информацией, а именно signature — в данном случае "root", startAddr и burnAddr заданные мною заранее, и lenght — длинна файла в шестнадцатиричной системе.

Также в конце файла появились два байта с контрольной суммой:

Смотрим оригинальную прошивку и... о чудо! Видим там похожую картину:

Заголовок ядра (начало прошивки)
Заголовок ядра (начало прошивки)
Место где начинается SquashFS
Место где начинается SquashFS
Контрольная сумма файловой системы в конце
Контрольная сумма файловой системы в конце

Как видим, каждый элемент прошивки (а в нашем случае она состоит из двух) имеет свой собственный заголовок и свою контрольную сумму. Бросается в глаза то, что вместо "root", параметр signature в заголовке файловой системы это "r6cr", а для ядра это "cr6c". Что ж, формат прошивки оказался совсем нехитрым.

И так, прогоняем сжатую файловую систему через cvimg, в этот раз указывая значения для startAddr и burnAddr из оригинальной прошивки:

./cvimg root squashfs-mod.sfs squashfs-mod.bin 0022000c 00300000

Стоит отметить что cvimg даёт значение для signature в зависимости от выбранного типа файла (root, linux, boot, all). Задать свое значение не получится. Так что вручную меняем «root» на «r6cr» в hex-редакторе и заливаем squash-mod.bin в прошивку:

sudo dd conv=notrunc bs=1 seek=2394130 if=squash-mod.bin of=firmware-mod.bin

Проверяем. Заходим в веб-интерфейс, выбираем нашу модифицированную прошивку и заливаем. Роутер больше не выдает никаких ошибок и процесс проходит успешно.

Убеждаемся, что наши изменения были внесены, пытаемся залогиниться с новым паролем и... вуаля!

DWR-M921 login: root
Password: 
RLX Linux version 2.0
         _           _  _
        | |         | ||_|                 
   _  _ | | _  _    | | _ ____  _   _  _  _ 
  | |/ || |\ \/ /   | || |  _ \| | | |\ \/ /
  | |_/ | |/    \   | || | | | | |_| |/    \
  |_|   |_|\_/\_/   |_||_|_| |_|\____|\_/\_/

For further information check:
http://processor.realtek.com/
#

Ну вот и все! Мы успешно модифицировали прошивку нашего роутера. Теперь я спокойно могу добавить автозапуск AdGuard’а. Надеюсь на то, что данная информация кому-нибудь да пригодится, и скорее всего данный метод подойдёт для любого устройства работающего на чипсете RTL8197F и ему подобных. До встречи!

P.S: Разбор работы cvimg.

Обнаружил исходный код cvimg в файлах SDK. Вот процесс расчета контрольной суммы для файловой системы:

В файле apmib.h:

#define WORD_SWAP(v) ((unsigned short)(((v>>8)&0xff) | ((v<<8)&0xff00))) // Функция, меняющая местами байты в переменной типа unsigned short (2 байта).

В файле cvimg.c:

// buf - буфер с содержимым исходного файла и двумя дополнительными байтами для контрольной суммы.

		if( !strcmp("root", argv[1])) {
			#define SIZE_OF_SQFS_SUPER_BLOCK 640
			unsigned int fs_len;
			fs_len = DWORD_SWAP((size - sizeof(IMG_HEADER_T) - sizeof(checksum) - SIZE_OF_SQFS_SUPER_BLOCK));
			// fs_len - Разность размера оригинального файла и заданого размера суперблока squashfs (640). Байты поменяны местами функцией WORD_SWAP().
			memcpy(buf + 8, &fs_len, 4); // Записывает значение переменной fs_len в буфер, начиная с восьмого байта.
		}

static unsigned short calculateChecksum(char *buf, int len) // len - длинна исходного файла.
{
	int i, j;
	unsigned short sum=0, tmp;

	j = (len/2)*2; // j = размер исходного файла. Если число нечётное, то при делении на два дробная часть упраздняется, так как j является переменной типа int, которая допускает только целые числа, и в результате получается число на единицу меньше исходного.

	for (i=0; i<j; i+=2) {
		tmp = *((unsigned short *)(buf + i));
		sum += WORD_SWAP(tmp);
	} // Считывает по два байта из буфера, меняет их местами функцией WORD_SWAP() и суммирует к переменной sum, пока не считает количество байт равное j.

	if ( len % 2 ) {
		tmp = buf[len-1];
		sum += WORD_SWAP(tmp);
	} // Если len нечетное число, дополнительно считывает последний байт из буфера, "переворачивает" функцией WORD_SWAP и суммирует к переменной sum.
	return ~sum+1; // Инвертирует значение битов переменной sum, суммирует 1, и возвращает результат функции calculateChecksum().
}

		checksum = WORD_SWAP(calculateChecksum(buf, status.st_size)); // Считает контрольную сумму, меняет местами байты и записывает результат в переменную checksum.
		*((unsigned short *)&buf[size-sizeof(IMG_HEADER_T)-sizeof(checksum)]) = checksum; // Записывает значение переменной checksum в конец буфера.

Файлы на GitHub — cvimg, cvimg.c, apmib.h.

Tags:
Hubs:
Total votes 84: ↑84 and ↓0+84
Comments31

Articles