- Mitglied seit
- 10 Mai 2006
- Beiträge
- 15,298
- Punkte für Reaktionen
- 1,760
- Punkte
- 113
Das will ich mal nicht hoffen, dann hätte jemand Dir eine andere Datei als die von mir bereitgestellte untergeschoben. Dort ist bereits seit Mitte Dez. 2015 die Version 1.23.2 der Busybox enthalten - in beiden Archiven (sowohl für SquashFS 3 als auch 4).Auf meiner FB 7490 wurde vor etwa 1 Monat modfs-Starter injektion fs4 installiert, da ist keine busybox 1.23.2 sondern definitiv 1.21.1 .
1. Bei den NOR-Boxen wird zwar nur ein einzelnes File geschrieben, das ist aber die Kombination aus Kernel und SquashFS-Image. Das Zerpflücken in die passenden MTD-Partitionen übernimmt dann ein Scanner im Kernel, der sich anhand der Signatur eines SquashFS-Images den richtigen Beginn im Flash sucht.KingTutt schrieb:aber wenn ich das richtig verstehe, wird da einerseits nur der Kernel kopiert (und nicht das Filesystem) und andererseits frage ich mich, ob das Kopieren nach mtd1 für NAND-Boxen überhaupt noch zutreffend ist?
2. Das ist bei den NAND-Boxen genau richtig verstanden, wobei es wohl keine Möglichkeit gibt, direkt aus der EVA heraus die NAND-Partitionen zu beschreiben. Genau deshalb macht es ja das AVM-Recovery-Programm (so jedenfalls meine Interpretation) auf diesem Weg.
-Um das umzusetzen, braucht es einen aktuellen Kernel (den kriegt man aus jedem Firmware-Image) und ein dazu passendes SquashFS-Image. Ob das bei einer Version ab 06.50 ein - mit dem Dummy-Header von 256 Byte als SquashFS maskiertes - ext2-Filesystem ist oder ob dabei der Header entfernt werden muß/kann, kann man ja mal mit dem AVM-Recovery-Programm vergleichen, das habe ich seit dem Erscheinen von 06.50 auch noch nicht geprüft. Der Scan-Code im Kernel legt die Vermutung nahe, daß auch das FS 1:1 aus einem Image verwendet werden könnte.
Ansonsten nimmt man halt das Filesystem-Image und entpackt es, ändert die dort enthaltenen Kommandos so ab, wie man es braucht, packt es wieder ordentlich zusammen (inkl. Dummy-Header, wenn nötig) und kopiert es hinter den Kernel in eine Datei. Entweder man entfernt mittels der Tools aus der Freetz-Toolchain oder mittels "dd" die Prüfsumme am Ende des Kernels (die letzten 8 Byte, wie wir später noch sehen werden).
Dann berechnet man ein paar Adressen und überträgt sein neues Image über die EVA in die Box ... so simpel läßt sich das zusammenfassen - aber wir gehen das einmal in einzelnen Schritten durch.
Eine 7490 hat normalerweise 256 MB Hauptspeicher, das sind hexadezimal 0x10000000 Byte (sieben Nullen) ... dieser beginnt an der Adresse 0x800000000 (die drei höchstwertigen Bits geben bei einer MIPS-Architektur den "address space" an, hier arbeiten wir mit "kseg0", was dem physikalischen Hauptspeicher entspricht -> unmapped and cached) und geht bis 0x8FFFFFFF (bzw. 0x90000000, wo eigentlich schon nichts mehr liegt oder was das erste Byte nach dem Hauptspeicher ist).
Damit das System uns jetzt nicht den eigenen Kernel und das Dateisystem überschreibt, verringern wir die Größe des Hauptspeichers entsprechend, am ersten damit "ausgeblendeten Byte" wird dann unsere "Datei" mit Kernel und Dateisystem "gespeichert". Wir nehmen uns also mal ein originales Firmware-Image für die 06.30 (der Einfachheit halber, dann müssen wir uns gar nicht über den Dummy-Header ab Kernel 3.10.73 den Kopf zerbrechen) und entpacken das:
Code:
# mkdir /var/unpack
# tar -C /var/unpack -xf FRITZ.Box_7490.113.06.30.image
# cd /var/unpack/
# find . -name *.image -exec stat -c "%n %s" '{}' \;
./var/tmp/kernel.image [COLOR="#008000"]1961992[/COLOR]
./var/tmp/filesystem.image 21917704
Code:
# dd if=var/tmp/kernel.image of=kernel.image bs=256 count=$(( 0x1df0 ))
7664+0 records in
7664+0 records out
1961984 bytes (1.9MB) copied, 0.107276 seconds, 17.4MB/s
# stat -c %s kernel.image
[COLOR="#008000"]1961984[/COLOR]
So, jetzt haben wir 1961984 Byte Kernel (0x1DF000 - eine schöne "gerade" Größe) und irgendein originales Dateisystem - schön zu wissen, aber wenn wir schon mal so weit sind, können wir auch gleich das Dateisystem passend ändern.
Also entpacken wir es erst einmal:
Code:
# unsquashfs3 var/tmp/filesystem.image
Filesystem on var/tmp/filesystem.image is ZLIB compressed (3:0)
Parallel unsquashfs: Using 2 processors
184 inodes (525 blocks) to write
[==================================================\] 525/525 100%
created 7 files
created 11 directories
created 91 symlinks
created 86 devices
created 0 fifos
# unsquashfs3 -d root squashfs-root/filesystem_core.squashfs var.tar
Filesystem on squashfs-root/filesystem_core.squashfs is ZLIB compressed (3:0)
Parallel unsquashfs: Using 2 processors
1 inodes (1 blocks) to write
[==================================================|] 1/1 100%
created 1 files
created 1 directories
created 0 symlinks
created 0 devices
created 0 fifos
# mv root/var.tar squashfs-root
# rm -r root
EDIT 25.07.2016: Ich habe vor einiger Zeit mal dem YourFritz-Repository ein "bin"-Verzeichnis hinzugefügt, in dem die Utilities auch für x64/x86 liegen (zusammen mit einer Signaturdatei, damit man sich von der Authentizität der Binärdateien überzeugen kann) - allerdings nur für die SquashFS-Version 4; hier müßte man also als Basis für das eigene Image dann auf eine Firmware-Version >= 06.50 bei AVM zurückgreifen. Da es gar nicht mehr so einfach ist, eine Version 06.30-Firmware irgendwo aufzutreiben, sehe ich das nicht als entscheidenden Nachteil.
Jedenfalls haben wir jetzt den ausgepackten Inhalt der "wrapper"-Partition vor uns:
Code:
# find squashfs-root/ -type f -exec ls -l '{}' \;
-rw-r----- 1 root root 30720 Jul 15 2015 squashfs-root/var.tar
-rw-r----- 1 root root 0 Jul 15 2015 squashfs-root/tmp/mtab
-rwxr-x--x 1 root root 6470 Jul 15 2015 squashfs-root/sbin/flash_update
-rwxrwxrwx 1 root root 686304 Jul 15 2015 squashfs-root/lib/libuClibc-0.9.33.2.so
-rwxrwxrwx 1 root root 31600 Jul 15 2015 squashfs-root/lib/ld-uClibc-0.9.33.2.so
-rw-r----- 1 root root 21389312 Jul 15 2015 squashfs-root/filesystem_core.squashfs
-rw-r----- 1 root root 361 Jul 15 2015 squashfs-root/etc/inittab
-rwxr-x--- 1 root root 449244 Jul 15 2015 squashfs-root/bin/busybox
# rm -r var
Schauen wir uns das "flash_update"-Skript von AVM einmal etwas genauer an, so finden wir am Beginn nur ein paar Zuweisungen von "Standard-Variablen" und ein paar Funktionen, die einmal eine Zeile aus /proc/mtd zerlegen und ein paar Meldungen ausgeben, die ohnehin nur jemand mit einer seriellen Konsole sehen würde. Danach wird es aber interessanter:
Code:
[...]
kernel_ram=`grep kernel_ram /proc/mtd`
rootfs_ram=`grep rootfs_ram /proc/mtd`
if [ -z ${kernel_ram_size} ]; then
exit 0
fi
get_mtd_size kernel_ram_size ${kernel_ram}
get_mtd_size rootfs_ram_size ${rootfs_ram}
if [ $((0x${kernel_ram_size})) -gt $((0x1000)) ]; then
if [ ${kernel_ram_size} == ${rootfs_ram_size} ]; then
exit 0
fi
else
exit 0
fi
[...]
Im weiteren Verlauf geht das Skript dann hin und kopiert den Inhalt der beiden RAM-Partitionen in das NAND-Flash ... aber das wollen wir ja eigentlich gar nicht. Uns würde es ja vollkommen reichen, wenn das Skript an dieser Stelle einfach unsere "wrapper"-Partition mounten und unsere Änderungen an deren Inhalt umsetzen würde.
Da wir außerdem "wissen", daß unser Skript nicht ebenfalls in die yaffs2-Partition kopiert und damit bei jedem Start erneut abgearbeitet wird, können wir uns auch die ganzen Tests sparen, die AVM dort vornimmt. Ein Skript für unsere Zwecke könnte z.B. in etwa so aussehen:
Code:
#! /bin/sh
#=========================================================================
#
# which file has to be copied to the yaffs2 partition
#
#=========================================================================
image="${1:-/filesystem_\$CUSTOM.squashfs}"
#=========================================================================
#
# modify active (a) or inactive (i) partition
#
#=========================================================================
modify_system=${2:-a}
#=========================================================================
#
# check for free space before copying the image to the target partition
# this should be set to 0, if we overwrite an existing file there, which
# is a little bit larger
#
#=========================================================================
check_space=${3:-1}
#=========================================================================
#
# some defaults
#
#=========================================================================
TMP="/var"
URLADER_ENV="/proc/sys/urlader/environment"
SYSTEM_SELECTOR="linux_fs_start"
FS_PARTITION="filesystem"
INACTIVE_PREFIX="reserved-"
MTDBLOCK="/dev/mtdblock"
MOUNTFS="$TMP/yaffs2"
INSTALL="sbin/flash_update"
REBOOT="/bin/busybox reboot"
WRAPPER="/wrapper"
ROOTFS="filesystem_core.squashfs"
CUSTOM="custom"
TARGETFILE="filesystem_$CUSTOM.squashfs"
#=========================================================================
#
# some helper functions
#
#=========================================================================
# dismount the yaffs2 partition securely
dismount()
{
[ $mounted -eq 1 ] && umount $MOUNTFS || mount -o remount,ro $MOUNTFS
}
# detect SquashFS version used in the target system to distinguish between
# kernel versions
detect_version()
{
[ "$(dd if=$MOUNTFS/$ROOTFS of=/proc/self/fd/1 count=2 bs=1 skip=28 2>/dev/null)" == $'\x04' ] && echo 3 || echo 2
}
# install extension handling for kernel versions 2.6.32
install_kernel_2()
{
cat >$MOUNTFS/$INSTALL <<ENDOFSCRIPT
#! /bin/sh
#
# version for kernel 2.6.32 - AVM firmware < 06.50 (/dev not bind mounted)
#
# start a detached shell to execute the $CUSTOM script, we've to exit here
# to let the init process continue
#
/usr/bin/nohup /bin/sh -x $WRAPPER/start_$CUSTOM.sh >$WRAPPER/dev/null 2>&1 &
exit 0
ENDOFSCRIPT
cat >$MOUNTFS/start_$CUSTOM.sh <<ENDOFSTART
#! /bin/sh
#
# let the OS settle a bit
#
sleep 5
#
# wait until tmpfs is mounted on /var
#
while true; do
grep -q "^tmpfs /var tmpfs" /proc/mounts 2>/dev/null && break
sleep 2
done
#
# check for "uninstall" request
# wait until start has finished in this case, we'll use the running "run_clock"
# as "decision helper"
#
if [ -f $WRAPPER/remove_$CUSTOM.flag ]; then
while true; do
pidof run_clock >/dev/null 2>&1 && break
done
cat >/var/remove_$CUSTOM.sh <<EOI
mount -o remount,rw $WRAPPER
rm $WRAPPER/remove_$CUSTOM.flag
rm $WRAPPER/remove_$CUSTOM.sh
rm $WRAPPER/start_$CUSTOM.sh
rm $WRAPPER/filesystem_$CUSTOM.squashfs
echo "exit 0" >$WRAPPER/$INSTALL
sync
mount -o remount,ro $WRAPPER
exit 0
EOI
nohup sh -x /var/remove_$CUSTOM.sh >/var/tmp/remove_$CUSTOM.out 2>&1 &
exit 0
fi
#
# mount our additional SquashFS image to /var/$CUSTOM
#
mkdir -p /var/$CUSTOM
mount -t squashfs $WRAPPER/$TARGETFILE /var/$CUSTOM
grep -q "^/dev/loop[0-9] /var/$CUSTOM squashfs" /proc/mounts || exit 1
#
# wait until lan device was started
#
while true; do
ifconfig lan 2>/dev/null | grep -q ".*UP.*RUNNING.*" && break
sleep 2
done
#
# start the init script (etc/init.d/rc.$CUSTOM) of the mounted extension
#
nohup sh -x /var/$CUSTOM/etc/init.d/rc.$CUSTOM >/var/tmp/rc.$CUSTOM.log 2>&1 &
ENDOFSTART
cat >$MOUNTFS/remove_$CUSTOM.sh <<ENDOFREMOVE
mount -o remount,rw $WRAPPER
touch $WRAPPER/remove_$CUSTOM.flag
sync
echo "The $CUSTOM extension will be removed during next restart."
exit 0
ENDOFREMOVE
chmod u+x $MOUNTFS/start_$CUSTOM.sh $MOUNTFS/remove_$CUSTOM.sh $MOUNTFS/sbin/flash_update
}
# install extension handling for kernel versions 3.10++
install_kernel_3()
{
cat >$MOUNTFS/$INSTALL <<ENDOFSCRIPT
#! /bin/sh
#
# version for kernel 3.10.73 - AVM firmware 06.50+ (assumption only)
#
# create a shell script to be called from a detached shell
# only /dev is writable here, it's very early after kernel was loaded
#
cat >/dev/delayed_start.sh <<'EOT'
#
# let the OS settle a bit
#
sleep 5
#
# wait until tmpfs is mounted on /var
#
while true; do
grep -q "^tmpfs /var tmpfs" /proc/mounts 2>/dev/null && break
sleep 2
done
#
# check for "uninstall" request
# wait until start has finished in this case, we'll use the running "run_clock"
# as "decision helper"
#
if [ -f $WRAPPER/remove_$CUSTOM.flag ]; then
while true; do
pidof run_clock >/dev/null 2>&1 && break
done
mount -o remount,rw $WRAPPER
rm $WRAPPER/remove_$CUSTOM.flag
rm $WRAPPER/remove_$CUSTOM.sh
rm $WRAPPER/filesystem_$CUSTOM.squashfs
echo "exit 0" >$WRAPPER/sbin/flash_update
sync
mount -o remount,ro $WRAPPER
exit 0 ]
fi
#
# mount our additional SquashFS image to /var/$custom
#
mkdir -p /var/$CUSTOM
mount -t squashfs $WRAPPER/$TARGETFILE /var/$CUSTOM
grep -q "^/dev/loop[0-9] /var/$CUSTOM squashfs" /proc/mounts || exit 1
#
# wait until lan device was started
#
while true; do
ifconfig lan 2>/dev/null | grep -q ".*UP.*RUNNING.*" && break
sleep 2
done
#
# start the init script (etc/init.d/rc.$CUSTOM) of the mounted extension
#
nohup sh -x /var/$CUSTOM/etc/init.d/rc.$CUSTOM >/var/tmp/rc.$CUSTOM.log 2>&1 &
EOT
#
# start a detached shell to execute the created script, we've to exit here
# to let the init process continue
#
nohup sh -x /dev/delayed_start.sh >/dev/delayed_start.out 2>&1 &
exit 0
ENDOFSCRIPT
cat >$MOUNTFS/remove_$CUSTOM.sh <<ENDOFREMOVE
mount -o remount,rw $WRAPPER
touch $WRAPPER/remove_$CUSTOM.flag
sync
echo "The $CUSTOM extension will be removed during next restart."
exit 0
ENDOFREMOVE
chmod u+x $MOUNTFS/remove_$CUSTOM.sh
}
#=========================================================================
#
# first we have to mount some things
#
#=========================================================================
# the procfs may be mounted multiple times without error, so we
# can do this without any further checks
mount -t proc proc /proc 2>/dev/null
#=========================================================================
#
# let's try to log to the "config" partition, if needed ... uncomment the
# following lines to enable debug output and don't forget to create the
# base directory "/log"
#
#=========================================================================
#log=$(sed -n -e "s/mtd\([0-9]\{1,2\}\):.*\"config\"\$/\1/p" /proc/mtd)
#mount -t yaffs2 $MTDBLOCK$log /log
#exec 2>/log/update_yaffs2.log
#set -x
#=========================================================================
#
# if tmpfs isn't mounted yet, we're running from memory loaded system
#
#=========================================================================
if ! grep -q "tmpfs $TMP" /proc/mounts; then
mount -t tmpfs tmpfs $TMP
tar xf /var.tar
if ! grep -q "devtmpfs .*/dev" /proc/mounts; then
tar cf $TMP/dev.tar /dev
mount -t tmpfs tmpfs /dev
tar xf $TMP/dev.tar
rm $TMP/dev.tar
fi
mount -t sysfs sysfs /sys
else
REBOOT="exit 1"
fi
#=========================================================================
#
# we check the image to be written first, if it's missing, there's
# nothing to do for us
#
#=========================================================================
eval image=$(echo $image)
filesize=$(stat -c "%s" $image)
[ ${#filesize} -eq 0 ] && $REBOOT # usually our image is missing
#=========================================================================
#
# now we've to select the right partition, where we should write into
#
#=========================================================================
current=$(sed -n -e "s/^$SYSTEM_SELECTOR\t\([01]\)\$/\1/p" $URLADER_ENV)
[ ${#current} -eq 0 ] && $REBOOT # missing or invalid selector value
[ $modify_system == a -o $modify_system == i ] || $REBOOT # invalid value
[ $modify_system == a ] && prefix="" || prefix="$INACTIVE_PREFIX"
mtd=$(sed -n -e "s/mtd\([0-9]\{1,2\}\):.*\"$prefix$FS_PARTITION\"\$/\1/p" /proc/mtd)
[ ${#mtd} -eq 0 ] && $REBOOT # filesystem partition not found
#=========================================================================
#
# now let's mount the partition somewhere, from this point on we will
# unmount the filesystem before we reboot the device
#
#=========================================================================
mounted=0
if [ $modify_system == a ] && [ "$REBOOT" == "exit 1" ]; then # running system
MOUNTFS=$WRAPPER
mount -o remount,rw $MOUNTFS || $REBOOT
else
mkdir -p $MOUNTFS
mount -t yaffs2 $MTDBLOCK$mtd $MOUNTFS || $REBOOT # do or die
mounted=1
fi
error_occured=0
#=========================================================================
#
# we could check the available space here, but it will be useless, if
# we're overwriting an existing file
#
#=========================================================================
if [ $check_space -eq 1 ]; then
freeblocks=$(df $MOUNTFS | grep $MOUNTFS | sed -n -e "s_^[^ ]*[ \t]*[0-9]*[ \t]*[0-9]*[ \t]*\([0-9]*\).*_\1_p")
if [ ${#freeblocks} -ne 0 ]; then
freesize=$(( freeblocks * 1024 ))
if [ $freesize -le $filesize ]; then
error_occured=1 # image too large
fi
else
error_occured=1 # unexpected output of "df"
fi
fi
[ $error_occured -ne 0 ] && dismount $MOUNTFS
[ $error_occured -ne 0 ] && $REBOOT
#=========================================================================
#
# now we can copy the image file to the partition
#
#=========================================================================
cp -a $image $MOUNTFS/$TARGETFILE
[ $? -ne 0 ] && error_occured=1 # error during copy operation
[ $error_occured -ne 0 ] && dismount $MOUNTFS
[ $error_occured -ne 0 ] && $REBOOT
#=========================================================================
#
# now we'll prepare the startup of own extensions, if necessary
#
#=========================================================================
version=$(detect_version)
if [ $version == 3 ]; then
install_kernel_3
else
install_kernel_2
fi
#=========================================================================
#
# now we'll unmount the yaffs2 partition and reboot the device
#
#=========================================================================
sync
dismount $MOUNTFS
$REBOOT
#=========================================================================
#
# end of script
#
#=========================================================================
Das gezeigte Skript schreibt in der o.a. Form die Datei "filesystem_custom.squashfs" aus dem Start-Dateisystem in die aktive yaffs2-Partition und prüft vorher, ob der dort noch freie Speicherplatz dafür ausreicht. Dasselbe Skript kann auch verwendet werden, um aus einer "normalen Shell" heraus die notwendigen Dateien in die yaffs2-Partition zu kopieren und parallel dazu gleich noch den Aufruf dieser Erweiterungen beim Start des Systems zu organisieren. Diese Version entscheidet anhand der Version des SquashFS für die "filesystem_core.squashfs" in der Zielpartition darüber, ob der Weg zum Starten der Erweiterung bei einem 2.6.32-Kernel oder bei einem 3.10.73-Kernel benötigt wird.
Jetzt müssen wir also nur unser neues Dateisystem entsprechend präparieren, wir nehmen als "custom image file" mal die SquashFS4-Version von "modfs-Starter" her - weil das "Zielsystem" eine 113.06.51 ist, obwohl unser "in-memory image" mit einer 113.06.30 erstellt wird:
Code:
# wget -O - http://yourfritz.de/inject_shellinabox_vr9_nand_sqfs4.tar | tar -x -O ./var/tmp/filesystem.image >squashfs-root/filesystem_custom.squashfs
Connecting to yourfritz.de (85.214.20.177:80)
- 100% |***********| 527k 0:00:00 ETA
Code:
# wget -O squashfs-root/update_yaffs2 http://yourfritz.de/update_yaffs2
Connecting to yourfritz.de (85.214.20.177:80)
update_yaffs2 100% |***********| 10979 0:00:00 ETA
# chmod a+x squashfs-root/update_yaffs2
# echo -e "null::sysinit:/bin/sh /update_yaffs2\nnull::shutdown:/bin/busybox umount /var/yaffs2\nnull::shutdown:/bin/busybox umount /log" >squashfs-root/etc/inittab
# rm squashfs-root/sbin/flash_update squashfs-root/filesystem_core.squashfs squashfs-root/etc/mtab
# mkdir squashfs-root/log squashfs-root/sys
# ln -s /var/tmp/mtab squashfs-root/etc/mtab
Code:
# mksquashfs3 squashfs-root/ filesystem.image
Parallel mksquashfs: Using 2 processors
Creating big endian 3.1 filesystem on filesystem.image, block size 65536.
[===================================|] 30/30 100%
Exportable Big endian filesystem, data block size 65536, compressed data, compressed metadata, compressed fragments, duplicates are removed
Filesystem size 1134.25 Kbytes (1.11 Mbytes)
67.63% of uncompressed filesystem size (1677.09 Kbytes)
Inode table size 1241 bytes (1.21 Kbytes)
26.47% of uncompressed inode table size (4688 bytes)
Directory table size 1660 bytes (1.62 Kbytes)
70.31% of uncompressed directory table size (2361 bytes)
Number of duplicate files found 1
Number of inodes 195
Number of files 7
Number of fragments 1
Number of symbolic links 91
Number of device nodes 86
Number of fifo nodes 0
Number of socket nodes 0
Number of directories 11
Number of uids 1
root (0)
Number of gids 0
Code:
# stat -c "%n %s" *.image
filesystem.image 1167360
kernel.image 1961984
Nun fügen wir den Kernel und das Dateisystem zusammen:
Code:
# cat kernel.image filesystem.image >memory.image
# stat -c %s memory.image
3129344
Zunächst einmal kopieren wir jetzt unsere neue Datei an eine Stelle, wo wir sie später noch erreichen können und dann überlegen wir uns, wie so eine FTP-Session am Ende aussehen müßte.
Code:
220 ADAM2 FTP Server ready
USER adam2
331 Password required for adam2
PASS adam2
230 User adam2 successfully logged in
SYST
215 AVM EVA Version 1.1964 0x0 0x740D
TYPE I
200 Type set to BINARY
MEDIA SDRAM
200 Media set to MEDIA_SDRAM
SETENV memsize 0x0fd04000
200 SETENV command successful
SETENV kernel_args_tmp mtdram1=0x8fd04000,0x90000000
200 SETENV command successful
TYPE I
200 Type set to BINARY
MEDIA SDRAM
200 Media set to MEDIA_SDRAM
P@SW
227 Entering Passive Mode (192,168,178,1,12,13)
STOR 0x8fd04000 0x90000000
150 Opening BINARY data connection
226 Transfer complete
Also müssen wir uns unseren eigenen "FTP-Client" basteln, alles was wir dazu unter einem Linux-System benötigen, sind die Kommandos "mkfifo", "nc" und eine nicht zu einfache Shell, selbst die "ash" aus der Busybox sollte ausreichen.
Ein entsprechendes Shell-Skript sähe z.B. so aus:
Code:
#! /bin/sh
#
# file to transfer to the router
#
filename="$1"
#
# some constants to be changed, if needed
#
box_ip=192.168.178.1
box_port=21
box_user=adam2
box_pass=adam2
passive_ftp="P@SW"
[ ${#TMP} -eq 0 ] && TMP=/tmp
tmpdir=$TMP/tmp_$(date +%s)_$$
writefifo=$tmpdir/write
readfifo=$tmpdir/read
storefifo=$tmpdir/store
outstream=7
instream=8
upstream=9
logstream=3
logfile=$0.log
envfile=$tmpdir/env
startaddress=0x80000000
#
# helper functions
#
read_ftp_response()
{
local line=" -" rc=0 instream="$1" log="$2"
while read -u $instream -r line; do
[ ! -z "$log" ] && echo "$line" >&$log
[ "${line:3:1}" != "-" ] && break
done
rc=$?
echo "$line"
return $rc
}
write_ftp_command()
{
local outstream="$2" cmd="$1" log="$3"
[ ! -z "$log" ] && echo "$cmd" >&$log
echo "$cmd" >&$outstream
}
login_to_box()
{
local instream="$1" outstream="$2" log="$3" lines=0
write_ftp_command "USER $box_user" $outstream $log
while [ $lines -lt 10 ]; do
line="$(read_ftp_response $instream $log)"
ec=${line:0:3}
if [ x$ec == x331 ]; then
write_ftp_command "PASS $box_pass" $outstream $log
else
if [ x$ec == x230 ]; then
return 0
fi
fi
lines=$(( lines++ ))
done
}
get_environment()
{
local instream="$1" outstream="$2" log="$3" lines=0
write_ftp_command "TYPE I" $outstream $log
line="$(read_ftp_response $instream $log)"
ec=${line:0:3}
if [ x$ec != x200 ]; then
return 1
fi
write_ftp_command "MEDIA SDRAM" $outstream $log
line="$(read_ftp_response $instream $log)"
ec=${line:0:3}
if [ x$ec != x200 ]; then
return 1
fi
write_ftp_command "$passive_ftp" $outstream $log
line="$(read_ftp_response $instream $log)"
ec=${line:0:3}
if [ x$ec == x227 ]; then
data_conn=$(echo $line | sed -n -e 's/.*(\([0-9]*\),\([0-9]*\),\([0-9]*\),\([0-9]*\),\([0-9]*\),\([0-9]*\)).*/data_ip=\1.\2.\3.\4 data_port=\$(( \5 * 256 + \6 ))/p')
if [ ${#data_conn} -eq 0 ]; then
return 1
fi
eval "$data_conn"
nc -d -w 60 $data_ip $data_port >$envfile &
data_connection=$!
sleep 1
write_ftp_command "RETR env" $outstream $log
line="$(read_ftp_response $instream $log)"
ec=${line:0:3}
if [ x$ec == x150 ]; then
line="$(read_ftp_response $instream $log)"
ec=${line:0:3}
if [ x$ec == x226 ]; then
if [ -d /proc/$data_connection ]; then
kill $data_connection 2>/dev/null &
wait $data_connection 2>/dev/null
fi
data_connection=""
echo $envfile
return 0
fi
else
return 1
fi
else
return 1
fi
}
upload_image()
{
local instream="$1" outstream="$2" log="$3" file="$4" memsize="$5" startaddr="$6" endaddr="$7"
eval "exec $upstream<>$storefifo"
if [ $? -ne 0 ]; then
return 1
fi
write_ftp_command "SETENV memsize $memsize" $outstream $logstream
line="$(read_ftp_response $instream $log)"
ec=${line:0:3}
if [ x$ec != x200 ]; then
eval "exec $upstream>&-"
rm $storefifo
return 1
fi
write_ftp_command "SETENV kernel_args_tmp mtdram1=$startaddr,$endaddr" $outstream $logstream
line="$(read_ftp_response $instream $log)"
ec=${line:0:3}
if [ x$ec != x200 ]; then
eval "exec $upstream>&-"
rm $storefifo
return 1
fi
write_ftp_command "TYPE I" $outstream $log
line="$(read_ftp_response $instream $log)"
ec=${line:0:3}
if [ x$ec != x200 ]; then
eval "exec $upstream>&-"
rm $storefifo
return 1
fi
write_ftp_command "MEDIA SDRAM" $outstream $log
line="$(read_ftp_response $instream $log)"
ec=${line:0:3}
if [ x$ec != x200 ]; then
eval "exec $upstream>&-"
rm $storefifo
return 1
fi
write_ftp_command "$passive_ftp" $outstream $log
line="$(read_ftp_response $instream $log)"
ec=${line:0:3}
if [ x$ec == x227 ]; then
data_conn=$(echo $line | sed -n -e 's/.*(\([0-9]*\),\([0-9]*\),\([0-9]*\),\([0-9]*\),\([0-9]*\),\([0-9]*\)).*/data_ip=\1.\2.\3.\4 data_port=\$(( \5 * 256 + \6 ))/p')
if [ ${#data_conn} -eq 0 ]; then
eval "exec $upstream>&-"
return 1
fi
eval "$data_conn"
nc -w 3 $data_ip $data_port <&$upstream 2>/dev/null 1>&2 &
data_connection=$!
sleep 1
write_ftp_command "STOR $startaddr $endaddr" $outstream $logstream
line="$(read_ftp_response $instream $log)"
ec=${line:0:3}
if [ x$ec == x150 ]; then
cat $file >$storefifo
line="$(read_ftp_response $instream $log)"
ec=${line:0:3}
if [ x$ec == x226 ]; then
if [ -d /proc/$data_connection ]; then
kill $data_connection 2>/dev/null &
wait $data_connection 2>/dev/null
fi
data_connection=""
eval "exec $upstream>&-"
rm $storefifo
return 0
fi
eval "exec $upstream>&-"
rm $storefifo
return 1
else
eval "exec $upstream>&-"
rm $storefifo
return 1
fi
else
eval "exec $upstream>&-"
rm $storefifo
return 1
fi
}
#
# check file size
#
if [ x$filename == x ]; then
echo "Missing file name."
exit 1
fi
filesize=$(stat -c %s $filename)
if [ $? -ne 0 ]; then
echo "Missing file '$filename'"
exit 1
fi
#
# check, if "mkfifo" and "nc" are present and usable
#
mkfifo 2>/dev/null
if [ $? -eq 127 ]; then
echo "Missing usable 'mkfifo' command."
exit 1
fi
nc 2>/dev/null
if [ $? -eq 127 ]; then
echo "Missing usable 'nc' command."
exit 1
fi
#
# build redirections for FTP with "nc"
#
mkdir -p $tmpdir
mkfifo $writefifo
rc=$?
if [ $rc -ne 0 ]; then
echo "Error $rc creating write fifo $writefifo."
rm -r $tmpdir
exit 1
fi
mkfifo $readfifo
rc=$?
if [ $rc -ne 0 ]; then
echo "Error $rc creating read fifo $readfifo."
rm $writefifo
rm -r $tmpdir
exit 1
fi
mkfifo $storefifo
rc=$?
if [ $rc -ne 0 ]; then
echo "Error $rc creating upload fifo $storefifo."
rm $writefifo
rm $readfifo
rm -r $tmpdir
exit 1
fi
eval "exec $outstream<>$writefifo"
rc=$?
if [ $rc -ne 0 ]; then
echo "Error $rc connecting write fifo to output stream."
rm $writefifo
rm $readfifo
rm $storefifo
rm -r $tmpdir
exit 1
fi
eval "exec $instream<>$readfifo"
rc=$?
if [ $rc -ne 0 ]; then
echo "Error $rc connecting read fifo to input stream."
eval "exec $outstream>&-"
rm $writefifo
rm $readfifo
rm $storefifo
rm -r $tmpdir
exit 1
fi
eval "exec $logstream<>$logfile"
rc=$?
if [ $rc -ne 0 ]; then
echo "Error $rc connecting log stream to log file."
eval "exec $instream>&-"
eval "exec $outstream>&-"
rm $writefifo
rm $readfifo
rm $storefifo
rm -r $tmpdir
exit 1
fi
#
# now open a connection to the box
#
nc $box_ip $box_port <&$outstream >&$instream 2>/dev/null &
control_connection=$!
data_connection=""
line="$(read_ftp_response $instream $logstream)"
ec=${line:0:3}
if [ x$ec == x220 ]; then
login_to_box $instream $outstream $logstream
if [ $? -eq 0 ]; then
write_ftp_command "SYST" $outstream $logstream
line="$(read_ftp_response $instream $logstream)"
ec=${line:0:3}
if [ x$ec == x215 ]; then
syst=$(echo "$line" | sed -n -e "s/.*\(AVM EVA\).*/\1/p")
if [ ${#syst} -ne 0 ]; then
echo "Found AVM bootloader: ${line:4}"
environment=$(get_environment $instream $outstream $logstream)
if [ $? -eq 0 ]; then
hwrev=$(sed -n -e "s/^HWRevision *\(.*\)\r\$/\1/p" $environment)
echo "Found hardware revision: $hwrev"
memsize=$(sed -n -e "s/^memsize *\(.*\)\r\$/\1/p" $environment)
echo "Memory size is $memsize $(printf "(%u MB)" $(( $memsize / 1024 / 1024 )))"
echo "Image size is $(printf "0x%06x" $filesize) $(printf "(%u MB)" $(( filesize / 1024 / 1024 )))"
setmemsize=$(printf "0x%08x" $(( memsize - filesize )))
echo "Setting temporary memory size to: $setmemsize"
imagestartaddr=$(printf "0x%08x" $(( startaddress + setmemsize )))
imageendaddr=$(printf "0x%08x" $(( startaddress + memsize )))
echo "Setting temporary kernel args to: mtdram1=$imagestartaddr,$imageendaddr"
upload_image $instream $outstream $logstream $filename $setmemsize $imagestartaddr $imageendaddr
if [ $? -eq 0 ]; then
echo "Image uploaded to device."
fi
fi
else
echo "Unexpected system found: ${line:4}"
fi
fi
else
echo "Login failed."
fi
else
echo "Error connecting to FRITZ!Box boot loader."
fi
if [ ${#data_connection} -ne 0 ]; then
if [ -d /proc/$data_connection ]; then
kill $data_connection 2>/dev/null &
wait $data_connection 2>/dev/null
fi
fi
if [ ${#control_connection} -ne 0 ]; then
if [ -d /proc/$control_connection ]; then
kill $control_connection 2>/dev/null &
wait $control_connection 2>/dev/null
fi
fi
eval "exec $logstream>&-"
eval "exec $instream>&-"
eval "exec $outstream>&-"
rm $writefifo
rm $readfifo
rm -r $tmpdir
exit 1
Das Skript wird mit dem Namen der zu verwendenden Image-Datei als Parameter aufgerufen und berechnet dann die Werte anhand von "memsize" und der Image-Größe gleich selbst, das spart Vorarbeiten. Allerdings erwartet es eine FRITZ!Box, die sich bereits im FTP-Modus des Bootloaders befindet. Das erreicht man z.B. durch die Verwendung eines falschen Recovery-Programms ... das hat dann keine andere Aufgabe, als die Box in diesen Modus zu bringen.
Das hilft zwar dem "nur Windows"-Benutzer noch nicht weiter, aber das gezeigte Skript sollte auf fast jedem beliebigen Linux-Host (von der STB bis zum RasPi) laufen können ... das ist ja auch schon etwas. Unter Windows baut man sich dann halt mit der PowerShell und dem NetClient der .NET-Library ein passendes Skript zusammen ... wobei ich auch niemanden von der Benutzung des ruKernelTools abhalten will, wenn dieses auch mit solchen Images umgehen können sollte (was ich nicht testen werde).
BTW ... das, was ich oben in einzelnen Schritten und Kommandos erläutert habe, kann man natürlich ebenfalls automatisieren, damit man diese Kommandos nicht jedesmal von Hand ausführen muß. Allerdings braucht es (normalerweise) diesen ganzen Aufwand auch nur ein einziges Mal, denn man hat dann ja ein Grundgerüst für die Modifikation der Box und muß nur noch jeweils die "Nutzlast" (also die Datei "filesystem_custom.squashfs") darin ersetzen, bevor man das SquashFS wieder zusammenpackt und hinter den Kernel kopiert ... das Ergebnis ist dann ja wieder die auf die Box zu ladende Datei. Die ganzen anderen Arbeiten zur Vorbereitung fallen nur einmalig an.
Zuletzt bearbeitet: