#!/bin/bash exec &2 exit 1 fi } # Form an associative lookup for the section numbers in the ELF symbol table. # Uses 8 character 0 expanded hexadecimal key for ease of consumption. __sectionmap_init() { readelf -W --headers "$1" | \ awk ' { sub("\\[", ""); sub("\\]", ""); } ($1 ~ /^[0-9][0-9]*/) { printf("%08x %s %s %s\n", int($1), $2, $3, $4); } ' | \ { while read section_num section_name section_type section_vma do echo "sectionmap_$section_num='$section_name'" echo "sectionvma_$section_num='$section_vma'" case "$section_type" in REL|RELA) section_relocation="$section_type" ;; esac done echo "section_relocation='$section_relocation'" } } sectionmap_init() { eval $(__sectionmap_init "$1") } sectionmap() { eval RET="\$sectionmap_$1" if [ "$RET" = '' ]; then echo "sectionmap: $1: invalid section" 1>&2 exit 1 fi } sectionvma() { eval RET="\$sectionvma_$1" if [ "$RET" = '' ]; then echo "sectionvma: $1: invalid section" 1>&2 exit 1 fi } # Read and parse the hex-dump output. hex="[0-9a-f]" hex_8="$hex$hex$hex$hex$hex$hex$hex$hex" hexspc="[0-9a-f ]" hexspc_8="$hexspc$hexspc$hexspc$hexspc$hexspc$hexspc$hexspc$hexspc" raw32() { readelf --hex-dump "$2" "$1" 2>/dev/null | sed \ -e '/^Hex/d' -e '/^$/d' -e '/^ *NOTE/d' \ -e 's/ *[^ ][^ ]* *\('"$hex_8"'\) \('"$hexspc_8"'\) \('"$hexspc_8"'\) \('"$hexspc_8"'\) .*/\1 \2 \3 \4 /' \ -e 's/\('"$hex$hex"'\)\('"$hex$hex"'\)\('"$hex$hex"'\)\('"$hex$hex"'\) /\4\3\2\1 /g' \ -e 's/ $//g' -e 's/ /\n/g' } #-e 's/\([^ ][^ ][^ ][^ ][^ ][^ ][^ ][^ ]\) \([^ ][^ ][^ ][^ ][^ ][^ ][^ ][^ ]\) /\2\1 /g' \ rela() { #file="$(basename "$1")" file="$1" # Read relocation information for a 64bit binary. Each relocation entry # is 3 long longs so we collect 6 quads here. Note that the dump is in # listed in increasing byte order not withstanding the quad split. # # The record says to take the value of add and # shove that into in the segment of the . # # Format: # 64 bits # 32 bits # 32 bits # 64 bits raw32 "$1" ".rela$SECTION" | \ { a1=''; a2=''; a3=''; a4=''; a5='' while read a6 do [ "$a1" = '' ] && { a1="$a6"; continue; } [ "$a2" = '' ] && { a2="$a6"; continue; } [ "$a3" = '' ] && { a3="$a6"; continue; } [ "$a4" = '' ] && { a4="$a6"; continue; } [ "$a5" = '' ] && { a5="$a6"; continue; } #echo ">$a1< >$a2< >$a3< >$a4< >$a5< >$a6<" 1>&2 #echo "type<$a3> symbol<$a4> offset<$a2$a1> addr<$a6a5>" 1>&2 symbolmap "$a4"; section_num="$RET" #echo "section_num<$section_num>" 1>&2 sectionmap "$section_num"; section="$RET" sectionvma "$section_num"; vma="$RET" #echo "section<$section> vma<$vma>" 1>&2 # Adjust the segment addressing by the segment offset. printf -v addr "%u" "0x$a6$a5" printf -v vma "%u" "0x$vma" let offset="$addr + $vma" printf -v offset "%x" "$offset" echo "$file-$section-$offset" a1=''; a2=''; a3=''; a4=''; a5='' done } | sed -e 's/-00*\([0-9a-f]\)/-\1/' } # Form an associative lookup for the raw contents for an ELF section. # Uses 8 character 0 expanded hexadecimal key for ease of consumption. contentmap_init() { raw32 "$1" "$2" >"$tmp" let offset=0 while read value do printf -v offset_hex "%08x" $offset eval contentmap_$offset_hex=\'$value\' let offset="$offset + 4" done <"$tmp" rm -f "$tmp" } contentmap() { eval RET="\$contentmap_$1" if [ "$RET" = '' ]; then echo "contentmap: $1: invalid offset" 1>&2 exit 1 fi } rel() { # Load up the current contents of the $SECTION segment # as the offsets (see below) are recorded there and we will need # those to calculate the actuall address. contentmap_init "$1" "$SECTION" #file="$(basename "$1")" file="$1" # Read relocation information for a 32bit binary. Each relocation entry # is 3 longs so we collect 3 quads here. Note that the dump is in # listed in increasing byte order not withstanding the quad split. # # The record says to take the value of and add that to the # existing contents of in the segment of the . # # Format: # 32 bits # 24 bits # 8 bits raw32 "$1" ".rel$SECTION" | \ { a1='' while read a2 do [ "$a1" = '' ] && { a1="$a2"; continue; } #echo ">$a1< >$a2<" contentmap "$a1"; offset="$RET" symbolmap "00${a2%??}"; section_num="$RET" sectionmap "$section_num"; section="$RET" sectionvma "$section_num"; vma="$RET" #echo ">$a1< >$a2< >$offset< >$section<" echo "$file-$section-$offset" a1='' done } | sed -e 's/-00*\([0-9a-f]\)/-\1/' } tmp=$(mktemp --tmpdir "retpoline-extract-XXXXXX") disassemble() { local object="$1" local src="$2" local options="$3" local selector="$4" objdump $options --disassemble --no-show-raw-insn "$object" | \ awk -F' ' ' BEGIN { file="'"$object"'"; src="'"$src"'"; } /Disassembly of section/ { segment=$4; sub(":", "", segment); } /^[0-9a-f][0-9a-f]* <.*>:/ { tag=$0; sub(".*<", "", tag); sub(">.*", "", tag); } $0 ~ /(call|jmp)q? *\*0x[0-9a-f]*\(%rip\)/ { next } $0 ~ /(call|jmp)q? *\*.*%/ { sub(":", "", $1); if ('"$selector"') { offset=$1 $1=tag print(file "-" segment "-" offset " " src " " segment " " $0); } } ' } # Accumulate potentially vunerable indirect call/jmp sequences. We do this # by examining the raw disassembly for affected forms, recording the location # of each. case "$bit16" in '') disassemble "$object" "$src" '' 'segment != ".init.text"' ;; *) disassemble "$object" "$src" '--disassembler-options=i8086' 'segment != ".init.text" && segment != ".text32" && segment != ".text64"' disassemble "$object" "$src" '--disassembler-options=i386' 'segment == ".text32"' disassemble "$object" "$src" '--disassembler-options=x86-64' 'segment == ".text64"' ;; esac | sort -k 1b,1 >"$object.ur-detected" [ ! -s "$object.ur-detected" ] && rm -f "$object.ur-detected" # Load up the symbol table and section mappings. symbolmap_init "$object" sectionmap_init "$object" # Accumulate annotated safe indirect call/jmp sequences. We do this by examining # the $SECTION sections (and their associated relocation information), # each entry represents the address of an instruction which has been marked # as ok. case "$section_relocation" in REL) rel "$object" ;; RELA) rela "$object" ;; esac | sort -k 1b,1 >"$object.ur-safe" [ ! -s "$object.ur-safe" ] && rm -f "$object.ur-safe" # We will perform the below join on the summarised and sorted fragments # formed above. This is performed in retpoline-check. #join -v 1 -j 1 "$tmp.extracted" "$tmp.safe" | sed -s 's/[^ ]* *//' rm -f "$tmp"