Initial import of ddsect
authorAntonio Ospite <ospite@studenti.unina.it>
Wed, 17 Oct 2012 13:02:52 +0000 (15:02 +0200)
committerAntonio Ospite <ospite@studenti.unina.it>
Wed, 17 Oct 2012 15:11:19 +0000 (17:11 +0200)
.gitignore [new file with mode: 0644]
README [new file with mode: 0644]
ddsect.sh [new file with mode: 0755]
example.map [new file with mode: 0644]
test.sh [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..220aa08
--- /dev/null
@@ -0,0 +1,2 @@
+*.dump
+*.bin
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..0c292b2
--- /dev/null
+++ b/README
@@ -0,0 +1,14 @@
+ddsect is a data dump dissecting tool based on dd.
+
+It can be used, for example, to dissect firmware files after their structure
+has been derived with tools like binwalk (https://code.google.com/p/binwalk/),
+or to split a raw flash dump into its MTD partitions.
+
+The format for describing each memory section in a map file is:
+
+  HexStartingAddres-HexEndingAddress : "name"
+
+just like the output of the MTD driver in linux.
+
+Empty lines and lines starting with the '#' character will be ignored
+in the map file.
diff --git a/ddsect.sh b/ddsect.sh
new file mode 100755 (executable)
index 0000000..b1c5122
--- /dev/null
+++ b/ddsect.sh
@@ -0,0 +1,157 @@
+#!/bin/sh
+#
+# ddsect - dissect (and reassemble) raw data dumps using a memory map file
+#
+# Copyright (C) 2012  Antonio Ospite <ospite@studenti.unina.it>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+set -e
+
+echo_exec()
+{
+  echo $@
+  $@
+}
+
+parse_map_file()
+{
+  MAP_FILE="$1"
+
+  cat $MAP_FILE | sed -e 's/#.*$//' -e '/^[[:space:]]*$/d' | tr -d ' ' |
+  while read line;
+  do
+    RANGE=$(echo $line | cut -d ':' -f 1)
+    START_ADDRESS=$(( $(echo $RANGE | cut -d '-' -f 1) ))
+    END_ADDRESS=$(( $(echo $RANGE | cut -d '-' -f 2) ))
+
+    NAME=$(echo $line | cut -d ':' -f 2 | sed -e 's/^"//'  -e 's/"$//')
+
+    SIZE=$(($END_ADDRESS - $START_ADDRESS))
+
+    SIZE_KiB=$(($SIZE / 1024))
+
+    echo "${NAME},${START_ADDRESS},${END_ADDRESS},${SIZE},${SIZE_KiB}"
+  done
+}
+
+print_map_info()
+{
+  MAP_FILE="$1"
+
+  (
+  echo "NAME START_ADDRESS END_ADDRESS SIZE SIZE_KiB"
+  parse_map_file $1
+  ) | column -s " ," -t
+}
+
+split_image()
+{
+  IMAGE_FILE="$1"
+  MAP_FILE="$2"
+
+  IFS=','
+  parse_map_file $MAP_FILE |
+  while read NAME START_ADDRESS END_ADDRESS SIZE SIZE_KiB;
+  do
+    echo_exec dd ibs=1 skip=$START_ADDRESS count=$SIZE if="$IMAGE_FILE" of="${NAME}.bin"
+  done
+
+}
+
+join_image()
+{
+  MAP_FILE="$1"
+  IMAGE_FILE="$2"
+
+  IFS=','
+  parse_map_file $MAP_FILE |
+  while read NAME START_ADDRESS END_ADDRESS SIZE SIZE_KiB;
+  do
+    echo_exec dd obs=1 seek=$START_ADDRESS count=$SIZE conv=notrunc if="${NAME}.bin" of="$IMAGE_FILE"
+  done
+}
+
+usage()
+{
+  cat 1>&2 << EOM
+usage: $(basename "$0") <OPTION> [ARGS]
+
+Where OPTION and ARGS are:
+
+       info <MAP_FILE>                         show details of the map file
+       split <INPUT_IMAGE> <MAP_FILE>          split sections as per MAP_FILE
+       join <MAP_FILE> <OUTPUT_IMAGE>          join sections as per MAP_FILE
+       -h|--help|help                          show this help message
+EOM
+}
+
+check_option_argument()
+{
+  ARGUMENT="$1"
+  DESCRIPTION="$2"
+  [ "x$ARGUMENT" != "x" ] || { echo "No $DESCRIPTION passed." 1>&2; usage; exit 1; }
+}
+
+check_input_file_argument()
+{
+  FILE="$1"
+  FILE_DESCRIPTION="$2"
+  check_option_argument "$FILE" "$FILE_DESCRIPTION"
+  [ -r "$FILE" ] || { echo "Invalid $FILE_DESCRIPTION." 1>&2; exit 1; }
+}
+
+command -v dd >/dev/null 2>&1 || { echo "$(basename "$0"): command 'dd' is missing." 1>&2 ; exit 1; }
+
+case $1 in
+  info)
+    MAP_FILE="$2"
+    check_input_file_argument "$MAP_FILE" "map file"
+
+    command -v column >/dev/null 2>&1 || { echo "$(basename "$0"): command 'column' is missing." 1>&2 ; exit 1; }
+
+    print_map_info "$MAP_FILE"
+    ;;
+
+  split)
+    INPUT_IMAGE="$2"
+    check_input_file_argument "$INPUT_IMAGE" "input image"
+
+    MAP_FILE="$3"
+    check_input_file_argument "$MAP_FILE" "map file"
+
+    split_image "$INPUT_IMAGE" "$MAP_FILE"
+    ;;
+
+  join)
+    MAP_FILE="$2"
+    check_input_file_argument "$MAP_FILE" "map file"
+
+    OUTPUT_IMAGE="$3"
+    check_option_argument "$OUTPUT_IMAGE" "output image"
+
+    join_image "$MAP_FILE" "$OUTPUT_IMAGE"
+    ;;
+
+  -h|--help|help)
+    usage
+    exit 0
+    ;;
+
+  *)
+    echo "Missing or unknown option." 1>&2
+    usage
+    exit 1;
+    ;;
+esac
diff --git a/example.map b/example.map
new file mode 100644 (file)
index 0000000..87ab630
--- /dev/null
@@ -0,0 +1,9 @@
+# Format for memory sections is
+# StartingAddress-EndingAddress : "name"
+#
+0x000000000000-0x000000020000 : "start"
+0x000000020000-0x000000040000 : "mtdss"
+0x000000040000-0x000000080000 : "rawstorage"
+0x000000080000-0x000000100000 : "userfs"
+0x000000100000-0x000000880000 : "bank_1"
+0x000000880000-0x000001000000 : "bank_2"
diff --git a/test.sh b/test.sh
new file mode 100755 (executable)
index 0000000..023acbf
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+set -e
+
+# A 16MiB test file
+dd if=/dev/urandom of=data.dump ibs=1M count=16
+
+./ddsect.sh info example.map
+./ddsect.sh split data.dump example.map
+./ddsect.sh join example.map new.dump
+
+md5sum data.dump new.dump
+
+rm -f data.dump new.dump *.bin