#!/bin/sh # # avrev - AVR reverse engineering helper # # Copyright (C) 2014 Michael Buesch # # 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 2 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, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # die() { echo "$*" >&2 exit 1 } # $1=program_name have_program() { which "$1" >/dev/null 2>&1 } # $1=program_name, ($2=description) assert_program() { local bin="$1" local desc="$2" [ -n "$desc" ] || local desc="$bin" have_program "$bin" || die "'$bin' not found. Please install $desc." } # $1=file_to_checksum csum() { local file="$1" sha1sum -b "$file" | cut -d' ' -f1 ||\ die "Checksum failed" } # $@=files_to_delete rm_temps() { if [ $opt_save_temps -eq 0 ]; then rm "$@" || die "Failed to remove temps: $*" fi } # Allocate an array variable # $1=array_variable_name array_alloc() { local var="$1" eval __${var}_LEN=0 } # Get array length # $1=array_variable_name array_length() { local var="$1" eval local length=\"\$__${var}_LEN\" printf '%s' "$length" } # Get an array element # $1=array_variable_name, $2=index array_elem_get() { local var="$1" local index="$2" eval local value=\"\$${var}_${index}\" printf '%s' "$value" } # Set an array element # $1=array_variable_name, $2=index, $3=value array_elem_set() { local var="$1" local index="$2" local value="$3" [ "$index" -ge "$(array_length "$var")" ] &&\ eval __${var}_LEN=$(expr $index + 1) # Extend length eval ${var}_${index}=\"\$value\" } # Append an element to an array # $1=array_variable_name, $2=value_to_append array_append() { local var="$1" local value="$2" array_elem_set "$var" "$(array_length "$var")" "$value" } # Collapse an array to a single string # $1=array_variable_name, [$2=element_prefix], [$3=element_suffix], # [$4=element_separator] array_collapse() { local var="$1" [ $# -ge 2 ] && local pfx="$2" || local pfx='' [ $# -ge 3 ] && local sfx="$3" || local sfx='' [ $# -ge 4 ] && local sep="$4" || local sep=' ' local length="$(array_length "$var")" local i=0 while [ $i -lt "$length" ]; do [ $i -eq 0 ] || printf '%s' "$sep" printf '%s%s%s' "$pfx" "$(array_elem_get "$var" "$i")" "$sfx" local i=$(expr $i + 1) done } usage() { echo "Usage: avrev [OPTIONS] INFILE" echo echo "INFILE: The input file to disassemble and analyse." echo " This may be a binary or ihex file (see --intype)." echo echo "Options:" echo " -I|--intype TYPE Input file type (default: binary)" echo " -m|--machine MACHINE Input file architecture (default: avr4)" echo " -c|--chip CHIP Input file chip type (default: m88)" echo echo " -d|--data-range RANGE Define a pure data range in program memory." echo " Example: -d 0x0-0x1F -d 0x100-0x1FF" echo " Defines byte range 0h-1Fh and 100h-1FFh as data." echo " -L|--label-file FILE Label file to pick label names from." echo " -C|--comment-file FILE Comment file to pick comments from." echo echo " -t|--save-temps Do not delete temporary files" echo " -i|--incdir INCDIR Directory containing .inc files." echo " (default: /usr/share/avra)" } setup_program_env() { export PATH=".:$PATH" assert_program sha1sum assert_program avr-objdump assert_program avr-objcopy assert_program avra assert_program avrasmpost } parse_args() { opt_opt_input_filetype="binary" opt_machine="avr4" opt_chip="m88" array_alloc opt_data_ranges array_alloc opt_label_files array_alloc opt_comment_files opt_save_temps=0 opt_incdir="/usr/share/avra" end=0 while [ $# -gt 0 -a $end -eq 0 ]; do case "$1" in -h|--help) usage exit 0 ;; -I|--intype) shift opt_opt_input_filetype="$1" ;; -m|--machine) shift opt_machine="$1" ;; -c|--chip) shift opt_chip="$1" ;; -d|--data-range) shift array_append opt_data_ranges "$1" ;; -L|--label) shift array_append opt_label_files "$1" ;; -C|--comment) shift array_append opt_comment_files "$1" ;; -t|--save-temps|-save-temps) opt_save_temps=1 ;; -i|--incdir) shift opt_incdir="$1" ;; *) end=1 break ;; esac [ $end -eq 0 ] && shift done [ $# -eq 1 ] || { usage exit 1 } opt_input_file="$1" [ -r "$opt_input_file" ] ||\ die "Could not read input file '$opt_input_file'" [ -d "$opt_incdir" ] ||\ die "Could not access INCDIR '$opt_incdir'" } run() { local dasm_file="${opt_input_file}.raw.asm" local postproc_file="${opt_input_file}.asm" local avra_hex="${opt_input_file}.hex" local avra_eep_hex="${opt_input_file}.eep.hex" local avra_obj="${opt_input_file}.obj" local avra_cof="${opt_input_file}.cof" local reasm_bin_file="${opt_input_file}.reassembled" echo "Disassembling '$opt_input_file'..." avr-objdump -m "$opt_machine"\ --full-contents\ --target="$opt_opt_input_filetype"\ --disassemble-all\ "$opt_input_file" > "$dasm_file" ||\ die "avr-objdump failed" echo "Postprocessing assembly..." local ranges="$(array_collapse opt_data_ranges "--data-range ")" local labels="$(array_collapse opt_label_files "--label-file ")" local comments="$(array_collapse opt_comment_files "--comment-file ")" avrasmpost -I "$dasm_file"\ -O "$postproc_file"\ $ranges $labels $comments \ "${opt_incdir}/${opt_chip}def.inc" ||\ die "avr-postproc failed" rm_temps "$dasm_file" echo "Re-assembling..." avra -I "$opt_incdir" "$postproc_file" >/dev/null ||\ die "avra failed" rm_temps "$avra_cof" "$avra_obj" "$avra_eep_hex" avr-objcopy -I ihex -O "$opt_opt_input_filetype"\ "$avra_hex" "$reasm_bin_file" ||\ die "avr-objcopy failed" rm_temps "$avra_hex" echo "Comparing..." opt_input_file_sum="$(csum "$opt_input_file")" echo "${opt_input_file}: ${opt_input_file_sum}" reasm_bin_file_sum="$(csum "$reasm_bin_file")" echo "${reasm_bin_file}: ${reasm_bin_file_sum}" if [ "x$opt_input_file_sum" = "x$reasm_bin_file_sum" ]; then echo "Ok" else die "ERROR: Checksum MISMATCH" fi rm_temps "$reasm_bin_file" } setup_program_env parse_args "$@" run exit 0