133 lines
4.9 KiB
Bash
Executable File
133 lines
4.9 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# sbat-info.tool - Dump SBAT information from .efi executable, with additional
|
|
# version and SBAT enforcement level information if the executable is Shim.
|
|
#
|
|
# Copyright (c) 2023, Michael Beaton. All rights reserved.<BR>
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
#
|
|
|
|
LIGHT_GREEN='\033[1;32m'
|
|
YELLOW='\033[1;33m'
|
|
RED='\033[0;31m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Note: binutils can simply be placed last on the path in macOS, to provide objcopy but avoid hiding native objdump (this script parses LLVM or GNU objdump output).
|
|
# Alternatively BINUTILS_PREFIX environment variable can be set to enabled prefixed co-existing GNU tools.
|
|
command -v "${BINUTILS_PREFIX}"objcopy >/dev/null 2>&1 || { echo >&2 "objcopy not found - please install binutils package or set BINUTILS_PREFIX environment variable."; exit 1; }
|
|
|
|
usage () {
|
|
echo "Usage: $(basename "$0") <efifile>"
|
|
echo " Display SBAT information, if present, for any .efi executable."
|
|
echo " If the file is a version of Shim, also dislay Shim version info and SBAT enforcement level."
|
|
}
|
|
|
|
has_section () {
|
|
"${BINUTILS_PREFIX}"objdump -h "$1" | sed -nr 's/^ *[0-9]+ ([^ ]+).*/\1/p' | grep "$2" 1>/dev/null
|
|
}
|
|
|
|
if [ -z "$1" ]; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
# https://unix.stackexchange.com/a/84980/340732
|
|
op_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'shim_info') || exit 1
|
|
|
|
# objcopy and objdump do not like writing to stdout when part of a pipe, so just write to temp files
|
|
# Note: these get created as empty files even if the section does not exist
|
|
"${BINUTILS_PREFIX}"objcopy -O binary -j ".sbat" "$1" "${op_dir}/sbat" || { rm -rf "${op_dir}"; exit 1; }
|
|
"${BINUTILS_PREFIX}"objcopy -O binary -j ".sbatlevel" "$1" "${op_dir}/sbatlevel" || { rm -rf "${op_dir}"; exit 1; }
|
|
"${BINUTILS_PREFIX}"objcopy -O binary -j ".data.ident" "$1" "${op_dir}/data.ident" || { rm -rf "${op_dir}"; exit 1; }
|
|
|
|
if ! has_section "$1" ".data.ident" ; then
|
|
IS_SHIM=0
|
|
echo -e "${YELLOW}Shim version info not present (probably not Shim)${NC}"
|
|
else
|
|
# Treat this section's presence as enough for the warning colours below
|
|
IS_SHIM=1
|
|
FILE_HEADER="$(head -n 1 "${op_dir}/data.ident")"
|
|
SHIM_HEADER="UEFI SHIM"
|
|
if [ ! "$FILE_HEADER" = "$SHIM_HEADER" ] ; then
|
|
echo -e "${RED}!!!Version header found '${FILE_HEADER}' expected '${SHIM_HEADER}'${NC}"
|
|
else
|
|
echo -e "${YELLOW}Found ${SHIM_HEADER} version header${NC}"
|
|
fi
|
|
|
|
# shellcheck disable=SC2016
|
|
VERSION="$(grep -a 'Version' "${op_dir}/data.ident" | sed -n 's/^\$Version: *\([^[:space:]]*\) *\$$/\1/p')"
|
|
if [ "$VERSION" = "" ] ; then
|
|
echo -e "${RED}Expected version number not present${NC}"
|
|
else
|
|
echo -e "Version: ${LIGHT_GREEN}${VERSION}${NC} (${VERSION}+, if user build)"
|
|
fi
|
|
|
|
# shellcheck disable=SC2016
|
|
COMMIT="$(grep -a 'Commit' "${op_dir}/data.ident" | sed -n 's/^\$Commit: *\([^[:space:]]*\) *\$$/\1/p')"
|
|
if [ "$COMMIT" = "" ] ; then
|
|
echo -e "${RED}Expected commit id not present${NC}"
|
|
else
|
|
echo "Commit: ${COMMIT}"
|
|
fi
|
|
fi
|
|
|
|
echo
|
|
echo -e "${YELLOW}SBAT (when tested by other files or by self):${NC}"
|
|
|
|
if ! has_section "$1" ".sbat" ; then
|
|
echo
|
|
echo -e "${RED}No .sbat section${NC}"
|
|
else
|
|
echo
|
|
echo -n -e "$LIGHT_GREEN"
|
|
cat "${op_dir}/sbat"
|
|
echo -n -e "$NC"
|
|
if ! cat "${op_dir}/sbat" | grep -E "[a-z]|[A-Z]" 1>/dev/null ; then
|
|
# Red for shim; yellow for other file
|
|
if [ "$IS_SHIM" -eq 1 ] ; then
|
|
echo -n -e "$RED"
|
|
else
|
|
echo -n -e "$LIGHT_GREEN"
|
|
fi
|
|
echo -e "Present but empty .sbat section"
|
|
fi
|
|
fi
|
|
|
|
echo
|
|
echo -e "${YELLOW}SBAT Level (when testing other files):${NC}"
|
|
|
|
if ! has_section "$1" ".sbatlevel" ; then
|
|
echo
|
|
# Yellow for shim (not present in earlier shims with .sbat, ~15.6); green for other file
|
|
if [ "$IS_SHIM" -eq 1 ] ; then
|
|
echo -n -e "$YELLOW"
|
|
else
|
|
echo -n -e "$LIGHT_GREEN"
|
|
fi
|
|
echo -n "No .sbatlevel section"
|
|
echo -e "$NC"
|
|
else
|
|
sbat_level_version=$(dd if="${op_dir}/sbatlevel" ibs=1 skip=0 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm -rf "${op_dir}"; exit 1; }
|
|
sbat_level_previous=$(dd if="${op_dir}/sbatlevel" ibs=1 skip=4 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm -rf "${op_dir}"; exit 1; }
|
|
sbat_level_latest=$(dd if="${op_dir}/sbatlevel" ibs=1 skip=8 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm -rf "${op_dir}"; exit 1; }
|
|
|
|
if [ "$sbat_level_version" -ne 0 ] ; then
|
|
echo -e "${RED}!!! Unexpected .sbatlevel version $sbat_level_version != 0 !!!${NC}"
|
|
fi
|
|
|
|
echo
|
|
echo -e "${YELLOW}Previous (more permissive boot, default):${NC}"
|
|
echo -n -e "$LIGHT_GREEN"
|
|
dd if="${op_dir}/sbatlevel" ibs=1 skip="$(($sbat_level_previous + 4))" count="$(($sbat_level_latest - $sbat_level_previous))" 2>/dev/null
|
|
echo -e "$NC"
|
|
|
|
echo -e "${YELLOW}Latest (stricter boot, optional):${NC}"
|
|
echo -n -e "$LIGHT_GREEN"
|
|
dd if="${op_dir}/sbatlevel" ibs=1 skip="$(($sbat_level_latest + 4))" 2>/dev/null
|
|
echo -e "$NC"
|
|
fi
|
|
|
|
rm -rf "${op_dir}"
|
|
|
|
exit 0
|