Well I followed through with writing mtd10 and rebooting after verifying that only mtd10 had changed and it looks like tplink firmware booted again. Here is the log:
Looking at it it seems that maybe mtd9 is one that needs to be written ?
Well I followed through with writing mtd10 and rebooting after verifying that only mtd10 had changed and it looks like tplink firmware booted again. Here is the log:
Looking at it it seems that maybe mtd9 is one that needs to be written ?
dd
is usually (there are exceptions) not suitable to be used on NAND, there nanddump
/ nandwrite
are required.
NAND isn't a block device, it doesn't abstract the BBT and ECC overhead from the actual data. As NAND is an unreliable storage medium by its very nature, this BBT/ ECC data does change over time (indicating bad sectors) - using dd
this way would restore these as well (which shouldn't be done) to a state of the distant past, but meanwhile sectors might have gone bad and you get a problem (as well as overwriting wear leveling information). This approach might 'work out fine' sometimes, but it will explode spectacularly if you hit the wrong sectors having gone bad and reduces the life time of your NAND significantly. Contrary to NOR flash, NAND degrades constantly, even by just reading it, so BBT/ ECC and wear-leveling are really important here, as well as mechanisms to skip over bad blocks.
No, the logic in the bootloader is to attempt to boot mtd10 if it looks good and if not it uses mtd9 as a failsafe.
The relevant log snippet is this:
Enter magic string to stop autoboot in 1 seconds
Creating 1 MTD partitions on "nand0":
0x000003600000-0x000006300000 : "mtd=0"
UBI: attaching mtd1 to ubi0
UBI: physical eraseblock size: 131072 bytes (128 KiB)
UBI: logical eraseblock size: 126976 bytes
UBI: smallest flash I/O unit: 2048
UBI: VID header offset: 2048 (aligned 2048)
UBI: data offset: 4096
UBI: attached mtd1 to ubi0
UBI: MTD device name: "mtd=0"
UBI: MTD device size: 45 MiB
UBI: number of good PEBs: 360
UBI: number of bad PEBs: 0
UBI: max. allowed volumes: 128
UBI: wear-leveling threshold: 4096
UBI: number of internal volumes: 1
UBI: number of user volumes: 3
UBI: available PEBs: 0
UBI: total number of reserved PEBs: 360
UBI: number of PEBs reserved for bad PEB handling: 3
UBI: max/mean erase counter: 3/1
Read 0 bytes from volume kernel to 84000000
No size specified -> Using max size (3555328)
Config not availabale
UBI: mtd1 is detached from ubi0
Creating 1 MTD partitions on "nand0":
0x000000900000-0x000003600000 : "mtd=0"
UBI: attaching mtd1 to ubi0
UBI: physical eraseblock size: 131072 bytes (128 KiB)
UBI: logical eraseblock size: 126976 bytes
UBI: smallest flash I/O unit: 2048
UBI: VID header offset: 2048 (aligned 2048)
UBI: data offset: 4096
UBI: attached mtd1 to ubi0
UBI: MTD device name: "mtd=0"
UBI: MTD device size: 45 MiB
UBI: number of good PEBs: 360
UBI: number of bad PEBs: 0
UBI: max. allowed volumes: 128
UBI: wear-leveling threshold: 4096
UBI: number of internal volumes: 1
UBI: number of user volumes: 3
UBI: available PEBs: 0
UBI: total number of reserved PEBs: 360
UBI: number of PEBs reserved for bad PEB handling: 3
UBI: max/mean erase counter: 2/0
Read 0 bytes from volume kernel to 84000000
No size specified -> Using max size (3428352)
## Booting kernel from FIT Image at 84000000 ...
Using 'config@ap.dk07.1-c1' configuration
Trying 'kernel@1' kernel subimage
Description: ARM OpenWrt Linux-3.14.77
Type: Kernel Image
Compression: gzip compressed
see how it tries loading 0x000003600000-0x000006300000 (what we call mtd10/rootfs_1) but then it gives a Config not availabale
, then loads 0x000000900000-0x000003600000 (what we call mtd9/rootfs)
so it tried to load our FIT kernel image, but it couldn't find the config in that image named 'config@ap.dk07.1-c1', because our openwrt build by default makes a single config in the FIT kernel image named 'config@1':
$ dumpimage -l openwrt-ipq40xx-generic-tplink_deco-m9plus-v2-initramfs-zImage.itb
Image contains unit addresses @, this will break signing
FIT description: ARM OpenWrt FIT (Flattened Image Tree)
Created: Sat Jan 4 02:04:32 2025
Image 0 (kernel-1)
Description: ARM OpenWrt Linux-6.6.69
Created: Sat Jan 4 02:04:32 2025
Type: Kernel Image
Compression: uncompressed
Data Size: 7102328 Bytes = 6935.87 KiB = 6.77 MiB
Architecture: ARM
OS: Linux
Load Address: 0x80208000
Entry Point: 0x80208000
Hash algo: crc32
Hash value: 84aad298
Hash algo: sha1
Hash value: 1dad17f5871b0cc82bb6a3af55057fde223b80ae
Image 1 (fdt-1)
Description: ARM OpenWrt tplink_deco-m9plus-v2 device tree blob
Created: Sat Jan 4 02:04:32 2025
Type: Flat Device Tree
Compression: uncompressed
Data Size: 19383 Bytes = 18.93 KiB = 0.02 MiB
Architecture: ARM
Hash algo: crc32
Hash value: c622be3c
Hash algo: sha1
Hash value: 8d5c80075973277a4dc7091dc11e32dc0961a843
Default Configuration: 'config@1'
Configuration 0 (config@1)
Description: OpenWrt tplink_deco-m9plus-v2
Kernel: kernel-1
FDT: fdt-1
easily fixed, just build openwrt FIT image with the desired config name 'config@ap.dk07.1-c1' by specifying this in your target/linux/ipq40xx/image/generic.mk:
DEVICE_DTS_CONFIG := config@ap.dk07.1-c1
DEVICE_DTS_DELIMITER := @
^^^ i have inside info that it is going to want the kernel itself to be named 'kernel@1' instead of 'kernel-1' so the second line fixes that as well.
Yes this is something to think about in some cases. But i ask you, have you ever had this happen pulling backups from a new device ??
I think you're going to want to use qsdk-ipq-factory-nand in your makefiel. Something like ..
IMAGE/nand-factory.img := append-ubi | qsdk-ipq-factory-nand | append-metadata
Yes. If you look at the Belkin rt3200 and the OKD, it's not even uncommon (NAND quality plays a large part in it as well). Yes, the bug itself was in OpenWrt not properly correcting a bitflip, but the OKD struck if there were issues (bitflips).
I'm willing to be wrong here, but I don't believe we have a QDSK sysupgrade tool available on stock m9+ fw for this to matter? all i see exposed is the usr/bin/nvrammanager
mechanism which requires a tplink-signed image (or an exploit payload, haha).
and the earily similar bootloader on the Deco M4RV3 (same typo in availabale
) didn't care about this (albeit its a NOR device so a slightly different path).
obviously its easy enough (for @meshing) to try, i just want to make sure im not missing some easy path that im not familiar with
I'm just hooking up UART on mine and reacted to your picture since it has no flash chip. Did you desolder it? The empty dual footprint to the right of the UART has an SPI flash on my v1 which is what I have dumped.
you'd know better ... I was just skimming and I should've read your post closer .. looking at it now, I believe you are correct.
Maybe you could actually contribute such info and update the missing wiki section ---> https://openwrt.org/docs/techref/mtd#mtd_vs_dd
It means I just got lucky until now.. I am adding nandump to my image and will backup again.
In 20+ years I have never had an issue ... to the contrary the nand utilities have gave me grief every time I've exercised them, possibly this is fixed now, but being your router has likely not been flash but a hand-full of times ... I think you're safe
I think I have gotten further now with your help @naf .
Now I think openwrt is booting but it wants to find a root partition name that it cannot find. I am guessing you already know what to change now ?
here is the log:
relevant log:
[ 1.783079] DSA: tree 0 setup
[ 1.789635] ubi0: attaching mtd10
[ 1.792420] ubi0: MTD device 10 is write-protected, attach in read-only mode
[ 2.042457] ubi0: scanning is finished
[ 2.049493] ubi0 warning: ubi_eba_init: cannot reserve enough PEBs for bad PEB handling, reserved 3, need 20
[ 2.050580] ubi0: attached mtd10 (name "rootfs_1", size 45 MiB)
[ 2.058479] ubi0: PEB size: 131072 bytes (128 KiB), LEB size: 126976 bytes
[ 2.064084] ubi0: min./max. I/O unit sizes: 2048/2048, sub-page size 2048
[ 2.071008] ubi0: VID header offset: 2048 (aligned 2048), data offset: 4096
[ 2.077878] ubi0: good PEBs: 360, bad PEBs: 0, corrupted PEBs: 0
[ 2.084651] ubi0: user volume: 3, internal volumes: 1, max. volumes count: 128
[ 2.090887] ubi0: max/mean erase counter: 5/2, WL threshold: 4096, image sequence number: 458199848
[ 2.097947] ubi0: available PEBs: 0, total reserved PEBs: 360, PEBs reserved for bad PEB handling: 3
[ 2.106910] ubi0: background thread "ubi_bgt0d" started, PID 88
[ 2.108160] block ubiblock0_1: created from ubi0:1(rootfs�[ 2.128763] UBIFS (ubi0:1): read-only UBI device
[ 2.128868] UBIFS error (ubi0:1 pid 1): ubifs_mount: cannot mount read-write - read-only media
[ 2.132899] mtd:ubi_rootfs: Can't lookup blockdev
[ 2.140881] VFS: Cannot open root device "mtd:ubi_rootfs" or unknown-block(0,253): error -2
[ 2.145696] Please append a correct "root=" boot option; here are the available partitions:
[ 2.153845] 1f00 1024 mtdblock0
[ 2.153857] (driver?)
[ 2.166311] 1f01 1024 mtdblock1
[ 2.166321] (driver?)
[ 2.172830] 1f02 1024 mtdblock2
[ 2.172839] (driver?)
[ 2.179330] 1f03 1024 mtdblock3
[ 2.179339] (driver?)
[ 2.185855] 1f04 512 mtdblock4
[ 2.185865] (driver?)
[ 2.192363] 1f05 512 mtdblock5
[ 2.192372] (driver?)
[ 2.198862] 1f06 1536 mtdblock6
[ 2.198871] (driver?)
[ 2.205382] 1f07 2048 mtdblock7
[ 2.205391] (driver?)
[ 2.211883] 1f08 512 mtdblock8
[ 2.211892] (driver?)
[ 2.218394] 1f09 46080 mtdblock9
[ 2.218404] (driver?)
[ 2.224932] 1f0a 46080 mtdblock10
[ 2.224942] (driver?)
[ 2.231762] 1f0b 9216 mtdblock11
[ 2.231771] (driver?)
[ 2.238360] 1f0c 17408 mtdblock12
[ 2.238370] (driver?)
[ 2.244972] 1f0d 3072 mtdblock13
[ 2.244981] (driver?)
[ 2.251557] fe00 6448 ubiblock0_1
[ 2.251565] (driver?)
[ 2.258150] List of all bdev filesystems:
[ 2.260405] squashfs
[ 2.260412]
[ 2.266743] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,253)
[ 2.268320] CPU0: stopping
[ 2.268330] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 6.6.69 #0
[ 2.268343] Hardware name: Generic DT based system
[ 2.268358] unwind_backtrace from show_stack+0x10/0x14
[ 2.268393] show_stack from dump_stack_lvl+0x40/0x4c
[ 2.268421] dump_stack_lvl from do_handle_IPI+0xec/0x120
[ 2.268439] do_handle_IPI from ipi_handler+0x18/0x20
[ 2.268454] ipi_handler from handle_percpu_devid_irq+0x78/0x134
[ 2.268472] handle_percpu_devid_irq from generic_handle_domain_irq+0x28/0x38
[ 2.268494] generic_handle_domain_irq from gic_handle_irq+0x74/0x88
[ 2.268514] gic_handle_irq from generic_handle_arch_irq+0x34/0x44
[ 2.268534] generic_handle_arch_irq from call_with_stack+0x18/0x20
[ 2.268563] call_with_stack from __irq_svc+0x80/0x98
[ 2.268584] Exception stack(0xc0e01f28 to 0xc0e01f70)
[ 2.268598] 1f20: 00000003 00000001 000089a4 40000000 00000000 c0e09f68
[ 2.268611] 1f40: c0e0d6c0 c0e09fa4 00000000 00000000 c0e09f08 00000000 00000007 c0e01f78
[ 2.268621] 1f60: c0a3b728 c0a3be90 60000013 ffffffff
[ 2.268629] __irq_svc from default_idle_call+0x2c/0x30
[ 2.268647] default_idle_call from do_idle+0x1d8/0x228
[ 2.268665] do_idle from cpu_startup_entry+0x28/0x2c
[ 2.268679] cpu_startup_entry from kernel_init+0x0/0x12c
[ 2.268696] kernel_init from arch_post_acpi_subsys_init+0x0/0x8
[ 2.268715] CPU1: stopping
[ 2.268724] CPU: 1 PID: 0 Comm: swapper/1 Not tainted 6.6.69 #0
[ 2.268736] Hardware name: Generic DT based system
[ 2.268744] unwind_backtrace from show_stack+0x10/0x14
[ 2.268772] show_stack from dump_stack_lvl+0x40/0x4c
[ 2.268798] dump_stack_lvl from do_handle_IPI+0xec/0x120
[ 2.268816] do_handle_IPI from ipi_handler+0x18/0x20
[ 2.268831] ipi_handler from handle_percpu_devid_irq+0x78/0x134
[ 2.268848] handle_percpu_devid_irq from generic_handle_domain_irq+0x28/0x38
[ 2.268867] generic_handle_domain_irq from gic_handle_irq+0x74/0x88
[ 2.268887] gic_handle_irq from generic_handle_arch_irq+0x34/0x44
[ 2.268905] generic_handle_arch_irq from call_with_stack+0x18/0x20
[ 2.268929] call_with_stack from __irq_svc+0x80/0x98
[ 2.268950] Exception stack(0xc109bf58 to 0xc109bfa0)
[ 2.268960] bf40: 00000003 00000001
[ 2.268973] bf60: 00000d44 40000000 00000001 c0e09f68 c1097000 c0e09fa4 00000000 00000000
[ 2.268986] bf80: 00000000 00000000 00000001 c109bfa8 c0a3b728 c0a3be90 60000013 ffffffff
[ 2.268994] __irq_svc from default_idle_call+0x2c/0x30
[ 2.269013] default_idle_call from do_idle+0x1d8/0x228
[ 2.269030] do_idle from cpu_startup_entry+0x28/0x2c
[ 2.269044] cpu_startup_entry from secondary_start_kernel+0x144/0x148
[ 2.269060] secondary_start_kernel from 0x80301214
[ 2.269076] CPU2: stopping
[ 2.269085] CPU: 2 PID: 0 Comm: swapper/2 Not tainted 6.6.69 #0
[ 2.269097] Hardware name: Generic DT based system
[ 2.269105] unwind_backtrace from show_stack+0x10/0x14
[ 2.269135] show_stack from dump_stack_lvl+0x40/0x4c
[ 2.269160] dump_stack_lvl from do_handle_IPI+0xec/0x120
[ 2.269178] do_handle_IPI from ipi_handler+0x18/0x20
[ 2.269193] ipi_handler from handle_percpu_devid_irq+0x78/0x134
[ 2.269209] handle_percpu_devid_irq from generic_handle_domain_irq+0x28/0x38
[ 2.269229] generic_handle_domain_irq from gic_handle_irq+0x74/0x88
[ 2.269248] gic_handle_irq from generic_handle_arch_irq+0x34/0x44
[ 2.269266] generic_handle_arch_irq from call_with_stack+0x18/0x20
[ 2.269291] call_with_stack from __irq_svc+0x80/0x98
[ 2.269312] Exception stack(0xc109df58 to 0xc109dfa0)
[ 2.269322] df40: 00000003 00000001
[ 2.269335] df60: 000009a4 40000000 00000002 c0e09f68 c1091000 c0e09fa4 00000000 00000000
[ 2.269348] df80: 00000000 00000000 00000002 c109dfa8 c0a3b728 c0a3be90 60000013 ffffffff
[ 2.269356] __irq_svc from default_idle_call+0x2c/0x30
[ 2.269374] default_idle_call from do_idle+0x1d8/0x228
[ 2.269391] do_idle from cpu_startup_entry+0x28/0x2c
[ 2.269406] cpu_startup_entry from secondary_start_kernel+0x144/0x148
[ 2.269421] secondary_start_kernel from 0x80301214
[ 2.650337] Rebooting in 1 seconds..
or does this log mean that mtd10 needs to be marked read-write somehow in the dts ?
bootloader hardcodes the name of the ubi rootfs volume but cant find it
[ 0.000000] Kernel command line: ubi.mtd=rootfs_1 root=mtd:ubi_rootfs rootfstype=squashfs rootwait
[ 2.128868] UBIFS error (ubi0:1 pid 1): ubifs_mount: cannot mount read-write - read-only media
[ 2.132899] mtd:ubi_rootfs: Can't lookup blockdev
[ 2.140881] VFS: Cannot open root device "mtd:ubi_rootfs" or unknown-block(0,253): error -2
[ 2.145696] Please append a correct "root=" boot option; here are the available partitions:
because the openwrt ubi image uses a different name:
$ ubireader_display_info openwrt-ipq40xx-generic-tplink_deco-m9plus-v2-squashfs-factory.ubi | grep "name:"
name: 'kernel'
name: 'rootfs'
name: 'rootfs_data'
(rootfs vs ubi_rootfs)
probably easier than changing the volume name in the openwrt build (or bootloader) is to just add some (additional) kernel arguments in the dts. something like this (credit to @caeklol)
chosen {
bootargs-append = " root=/dev/ubiblock0_1";
};
or maybe
chosen {
bootargs-append = " root=mtd:rootfs";
};
?
EDIT WARNING: changed the dts suggestions above
while I am compiling with the change, I think @naf with your exploit, I tried to get a shell and do ubidetach/ubiformat but the detach does not work as it returns resource busy. Does that mean I have to use some force mode or something ?
or does that mean that we can only flash with openwrt initramfs console ?
come to think of it (im not good at that) im not sure if we can detach the active rootfs. i would have tried finding processes with open files on rootfs, but stock has no lsof unfortunately. could kill
everything you see in ps
output that isn't an ssh shell maybe?
perhaps we can figure out how to get stock to boot into the failsafe mtd9 and use that to write mtd10 perhaps? we know how to do this on accident, just upload a bogus firmware to mtd10 and bootloader switches over. perhaps use that if we can't find something more elegant.
EDIT TO ADD: ls -l /proc/*/fd | grep -v pts | grep -v pipe | grep -v null | grep -v socket
maybe?
hmm. I think my main thing is that I have two more of these devices that I rather not open up and solder to get serial console to do all this. The beauty of your exploit is that I can get a shell without much effort so if we can do something there then that is the smoothest path to get openwrt on the other two devices.
(assuming we get my fork to get flashed and work properly first)
you're saying detach works from the serial console but not the ssh shell of the same running stock firmware?
rephrased: how did you write your image to mtd10?
I am saying the only way I can write mtd10 is from openwrt initramfs console that i get from tftpboot
. I have not been using your exploit shell.
And in the initramfs console there are no ubi devices to detach so I just run ubiformat/sync and I am done.