27 мая 2014 г.

Cisco ASA IOS 8.4(7) repack for QEMU

Было получено особо важное задание - по образу и подобию товарищей раздербанить образ IOS для Cisco ASA 8.4(7). По большому счету - все уже украдено до нас, и основной задачей было найти байты начала архива initrd.gz и ядра vmlinuz.
В качестве начальной аксиомы принимаем следующие вещи (об этом уже подумали умельцы на форуме по ссылке выше):
а) в бинарном файле прошивки (asa847-15-k8.bin) данные расположены следующим образом: сначала идет некая ненужная нам информация, затем файл vmlinuz, затем до конца файла initrd.
б) initrd в бинарнике находится в архивированном виде - .gz.

Таким образом, чтобы извлечь из прошивки оба необходимых нам для загрузки IOS файла, нужно знать биты, с которых начинать копирование vmlinuz и initrd
Для начала делаем шестнадцатеричный дамп всего файла с прошивкой:
# hexdump -C asa847-15-k8.bin > asa847-15-k8.hd
# ls -l | grep asa
-rwxr--r-- 1 root   root    24809472 май 26 11:34 asa847-15-k8.bin
-rw-r--r-- 1 root   root   122304006 май 26 13:56 asa847-15-k8.hd
Далее ищем по файлу дампа последовательность "1f 8b 08"
# grep "1f 8b 08" asa847-15-k8.hd
00165e00  1f 8b 08 08 22 e3 31 53  00 03 72 6f 6f 74 66 73  |....".1S..rootfs|
00a2d990  1f 8b 08 b0 36 d1 5d 2d  30 75 03 bb 82 ab 6b 13  |....6.]-0u....k.|
Почему именно эту последовательность? Выше отмечалось, что initrd заархивирован с помощью gzip, а файл .gz имеет magic number "1f 8b" (то есть любой gz-архив всегда начинается с этих битов). Третий бит - "08" - это одна из опций gzip. Как видно из примера, трех бит вполне хватает, чтобы до минимума снизить количество совпадению по дампу. Из соображений размера (initrd занимает бОльшую часть всей прошивки, ведь там расположено дерево каталогов операционки) и по подсказке "rootfs" используем адрес 00165e00 как точку отсчета для побитового копирования файла inird.gz.
В десятичной форме получим
# echo $((0x165e00))
1465856
Далее нужно узнать, где начинается файл ядра. Метода точно такая же, но не столь надежная, как в случае с gz. Опытным путем было определено, что vmlinuz в asa 8.4 начинается с последовательности "ea 05 00", таким образом
# grep "ea 05 00" asa847-15-k8.hd
0001a000  ea 05 00 c0 07 8c c8 8e  d8 8e c0 8e d0 31 e4 fb  |.............1..|
В десятичной форме адрес получается
# echo $((0x01a000))
106496
Итак, мы знаем откуда ядро начинается, и где заканчивается (см. пункт "а" аксиомы), то есть его длина будет равна
# echo $((1465856-106496))
1359360
Сейчас полученные цифры осталось вставить в скрипт, доступный, например, тут или тут, изменить по тексту имена файлов прошивки и получающихся после перепаковки файлов.
Получится примерно так:
#!/bin/bash
#
# Full credit goes to "dmz" of 7200emu.hacki forum for writing this patch
# http://7200emu.hacki.at/viewtopic.php?p=33838
#
# Thanks mate!
#

VERSION=4

IMAGE=$1
CWD=`pwd`
[ -z "$IMAGE" ] && IMAGE=$CWD/asa847-15-k8.bin

echo "Repack script version: $VERSION"

if [ ! -f "$IMAGE" ]; then
  echo "USAGE: repack.sh /path/to/asa/image"
  exit 1;
fi

XXD=`which xxd`
ISOLINUX_BIN=/usr/lib/ISOLINUX/isolinux.bin
MKISOFS=`which mkisofs`

if [ ! -x "$XXD" ]; then
  echo "ERROR: xxd command not found"
  echo "Install 'vim' or 'vim-enhanced' package to get it"
  exit 1;
fi
REATEISO=no

if [ -x "$MKISOFS" -a -e "$ISOLINUX_BIN" ]; then
  echo "Detected syslinux/cdrtools - ISO will be created"
  CREATEISO=yes
else
  echo "no syslinux/cdrtools - ISO creation skipped"
fi

BASE_NAME=`basename "$IMAGE"`
case "$BASE_NAME" in
  'asa847-15-k8.bin') # ASA 8.4(7)
    dd skip=106496 if="$IMAGE" of="$CWD/asa847-vmlinuz" bs=1 count=1359360
    dd skip=1465856 if="$IMAGE" of="$CWD/asa847-initrd-original.gz" bs=1
    TMP_DIR=`mktemp -d`
    pushd $TMP_DIR
      gunzip -c "$CWD/asa847-initrd-original.gz" | cpio -i --no-absolute-filenames --make-directories
      find . | cpio -o -H newc | gzip -9 > "$CWD/asa847-initrd-original.gz"
      sed -i -e "s/\(VERBOSE=\).*/\1yes/" etc/init.d/rcS
      sed -i -e "s/echo -n/echo/" etc/init.d/S10udev
      sed -i -e "49a/sbin/modprobe e1000 \&\& /sbin/modprobe e100 \&\& sleep 5 \&\& echo 'loaded.'" etc/init.d/S10udev
      sed -i -e "s#/sbin/ifup -a#for int in \`ifconfig -a | grep -o '^eth.'\`; do ifconfig \$int up promisc; done\n\tifconfig -a | awk '/^eth./{a=a\"-e \"\$1\" \"}END{print a}' >/tmp/lina_eth#" etc/init.d/S40network
     sed -i -e "s#^fi\$#fi\ngrep -q shell /proc/cmdline\n[ \$? == 0 ] \&\& echo '/bin/sh' >> /tmp/run_cmd#" asa/scripts/rcS
      sed -i -e "s#^fi\$#fi\nMULTI=\ngrep -q 'context admin' /mnt/disk0/.private/startup-config\n[ \$? == 0 ] \&\& MULTI=-m#" asa/scripts/rcS
      sed -i -e "s#lina_monitor -l\"#lina -t \$MULTI \`cat /tmp/lina_eth\`\"#" asa/scripts/rcS
      sed -i -e "/mount/d" asa/scripts/format_flash.sh
      sed -i -e "s#mount=0#if [ ! -e /dev/hda1 ]; then /asa/scripts/format_flash.sh /dev/hda1 0 0 /dev/hda; fi\nmount=0#" asa/scripts/rcS.common
      xxd -r -g 2 -c 16 - asa/bin/lina <<END
02716a0: a1c0 ab32 0a55 89e5 83e8 1283 f80b eb10
037f350: a1c0 ab32 0a55 89e5 83e8 1283 f80b eb10
06f4da0: 848b 0100 0083 3dc0 ab32 0aff 0f85 ae00
06f4df0: 85db c705 40a8 1a0a 0000 0000 0f84 a200
06f5170: 0085 c075 2be8 160b 98ff 8d95 f4f4 ffff
06f5c50: c30f 8589 0000 00c7 4424 084f 0200 00c7
06f5ce0: b801 0000 00d3 e0a9 0000 f43f 0f85 65ff
06f5d10: 1400 0000 e827 9ae0 0089 c290 8d74 2600
0c42a60: e8bb 3f00 0085 c00f 84b0 fbff ff8d 7600
0c42a70: e8ab 3f00 0085 c089 85e8 feff ff0f 849a
0c42b80: 5424 0889 4424 04c7 0424 0000 0000 e925
0c42b90: 0200 00e9 0bff ffff e813 9aff ff8d 7600
0c42db0: feff ff90 8d74 2600 8d46 02c7 45c0 0000
0c42dc0: 0000 8945 c4c7 45c8 0000 0000 e89f f5ff
0c42dd0: ff90 9090 e9ca fcff ff8d b426 0000 0000
0c4a670: 3bff 8db6 0000 0000 8b45 e4c7 45c0 0100
1502bf0: 83c4 245b 5dc3 6690 85db 75f2 a1c0 ab32
15505a0: e94b 0000 0098 60bb 098d b426 0000 0000
1550f50: c0ab 320a 83e8 1283 f80b eb0c 31c0 c9c3
1551060: c0ab 320a 83e8 1283 f80b eb0c 31c0 c9c3
1552c10: 42ee 83f8 0b0f 8589 1100 00ba 7160 bb09
1b6f350: 4f53 5449 4300 4153 4120 3535 3230 0043
END
      find . | cpio -o -H newc | gzip -9 > "$CWD/asa847-initrd.gz"
    popd
    rm -rf $TMP_DIR
    if [ "$CREATEISO" == "yes" ]; then
      TMP_DIR=`mktemp -d`
      pushd $TMP_DIR
        mkdir isolinux
        cp $ISOLINUX_BIN isolinux/
        cp $CWD/asa847-vmlinuz .
        cp $CWD/asa847-initrd.gz .
        cp $CWD/asa847-initrd-original.gz .
        cat >isolinux/isolinux.cfg <<EEND
serial 0
display boot.txt
prompt 1
default asa

label asa
  kernel /asa847_vmlinuz
  append initrd=/asa847_initrd.gz ide_generic.probe_mask=0x01 ide_core.chs=0.0:980,16,32 auto nousb console=ttyS0 bigphysarea=65536

label asa-orig
  kernel /asa847_vmlinuz
  append initrd=/asa847_initrd_original.gz ide_generic.probe_mask=0x01 ide_core.chs=0.0:980,16,32 auto nousb console=ttyS0 bigphysarea=65536

EEND
cat >isolinux/boot.txt <<EEND

^O09asa^O07      - ^O0fASA 8.4(7) (DEFAULT)^O07
^O09asa-orig^O07 - ^O0fASA 8.4(7) original^O07

Press <ENTER> to boot default image
Type 'asa shell<ENTER>' to gain shell access
Type 'asa-orig<ENTER>' to boot original initrd

EEND
        $MKISOFS -o $CWD/asa.iso -l \
          -b isolinux/isolinux.bin -c isolinux/boot.cat \
          -no-emul-boot -boot-load-size 4 -boot-info-table \
          ./
      popd
      rm -rf $TMP_DIR
    fi
    ;;
  *) # Default case
    echo "Version <$BASE_NAME> is not supported!"
    exit 1;
    ;;
esac
В итоге получится 
# ls -l | grep asa
-rwxr--r-- 1 krylatykhma domain users  24809472 май 26 11:34 asa847-15-k8.bin
-rw-r--r-- 1 root        root          23163175 май 27 09:20 asa847-initrd.gz
-rw-r--r-- 1 root        root          23161202 май 27 09:20 asa847-initrd-original.gz
-rw-r--r-- 1 root        root           1359360 май 27 09:19 asa847-vmlinuz
-rw-r--r-- 1 root        root          48093184 май 27 09:20 asa.iso
Эти файлы можно пытаться подсунуть qemu или напрямую (через параметры -initrd и -kernel) или через iso-образ.