#!/bin/bash # Converts little endian 4-byte unsigned integer given as spaced hex string # into decimal number (e.g. "00 D0 4C 00" -> 5033984) hex2dec() { # ATTENTION, 'strtonum' is gawk-specific, other awk versions like mawk # probably do not know it. echo "$1" | gawk '{ printf "%d\n", strtonum("0x" $4 $3 $2 $1) }' } # Extracts urlader.image candidates from input file, based on bootloader magic # string. Applies heuristic check for typical strings occurring in AVM # bootloaders. Detects older ADAM as well as newer EVA bootloaders. extract_bootloader() { echo -e "\nExtracting bootloader (urlader.image) ..." n=1 for offs in $(tools/hexgrep "$BOOTLOADER_MAGIC" "$input_file" | sed 's/:.*//'); do output_file="$output_dir/urlader.image-$((n++))" echo " $output_file - loader @ $offs" dd if="$input_file" of="$output_file" ibs=1 obs=8192 skip=$offs count=65536 2> /dev/null grep -iq 'entering passive mode' "$output_file" \ && grep -iq 'ftp server ready' "$output_file" \ && echo " file is a possible bootloader candidate" \ || echo " file is probably not a bootloader" done [ $n -eq 1 ] && echo " No bootloader candidate found in input file" } # Extracts kernel.image candidates from input file, based on kernel and # SquashFS magic strings. Applies heuristic check if kernel end offset # calculated from assumed length header field is close enough to subsequent # SquashFS start offset. Detects hidden root images used in ds26-enabled # firmwares, but currently no other image types. extract_kernel_filesystem() { echo -e "\nExtracting hidden root kernel + filesystem (kernel.image) ..." n=1 squashfs_lines="$(tools/hexgrep "$SQUASHFS_MAGIC" "$input_file")" squashfs_offs=($(echo "$squashfs_lines" | sed 's/:.*//')) squashfs_length=($(hex2dec "$(echo "$squashfs_lines" | sed -r 's/^[0-9]+:.{24}(.*)/\1/')")) kernel_lines="$(tools/hexgrep "$KERNEL_MAGIC" "$input_file")" kernel_offs=($(echo "$kernel_lines" | sed 's/:.*//')) kernel_length=($(hex2dec "$(echo "$kernel_lines" | sed -r 's/^[0-9]+:.{12}(.*)/\1/')")) k=0 while [ $k -lt ${#kernel_offs[*]} ]; do s=0 while [ $s -lt ${#squashfs_offs[*]} ]; do delta=$((squashfs_offs[s] - kernel_length[k] - kernel_offs[k])) [ $delta -le 0 -o $delta -gt 1024 ] && s=$((s+1)) && continue squashfs_pad=$(((4096 - squashfs_length[s] % 4096) % 4096)) dd_count=$((squashfs_offs[s] - kernel_offs[k] + squashfs_length[s] + squashfs_pad)) output_file="$output_dir/kernel.image-$((n++))" echo " $output_file - kernel @ ${kernel_offs[k]}, SquashFS @ ${squashfs_offs[s]}, total length = $dd_count" dd if="$input_file" of="$output_file" ibs=1 obs=8192 skip=${kernel_offs[k]} count=$dd_count 2> /dev/null "$mod_base/tools/tichksum" "$output_file" | sed 's/^/ /' break done k=$((k+1)) done [ $n -eq 1 ] && echo " No hidden root kernel + filesystem candidate found in input file" } # Parameter handling mod_base="." case $# in 1) input_file="$1" ;; 2) mod_base="$1" input_file="$2" ;; *) echo "usage: $0 [] " >&2 echo " - freetz base directory defaults to '.'" >&2 echo " - input file (e.g. recover exe or mtdblock dump) is to be searched for" echo " kernel.image and/or urlader.image" >&2 exit 1 esac # Magic byte sequences prepared for 'hexgrep' usage KERNEL_MAGIC="81 12 ED FE .. .. .. .." SQUASHFS_MAGIC="68 73 71 73 .. .. .. .. .. .. .. .." BOOTLOADER_MAGIC="00 90 80 40" output_dir="$(basename "$input_file" | sed -r 's/(.*)\.[^\.]*/\1/')" mkdir -p "$output_dir" extract_bootloader extract_kernel_filesystem