Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,29 @@ jobs:
path: |
target/*/release/avml
target/*/release/avml-minimal
armv7:
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7.0.1
- uses: Swatinem/rust-cache@a45951ff880207c249adf57334cf2e9bd81d6e1e # v2
- name: Cache ARMv7 build chain
uses: actions/cache@v6
with:
path: |
~/.cache/avml/arm-linux-musleabihf-cross
~/.cache/avml/musl-cross-make-armv7-227df8b99103f9c59f6570babf892978e293082f
key: armv7-build-chain-${{ runner.os }}-${{ hashFiles('eng/build-armv7.sh', 'eng/musl-cross-common.sh') }}
- name: build
run: eng/build-armv7.sh
- name: upload artifacts
uses: actions/upload-artifact@v7.0.1
with:
name: linux-armv7-artifacts
path: |
target/armv7-unknown-linux-musleabihf/release/avml
target/armv7-unknown-linux-musleabihf/release/avml-minimal
armv6b:
permissions:
contents: read
Expand All @@ -86,7 +109,7 @@ jobs:
path: |
~/.cache/avml/armeb-linux-musleabi-cross
~/.cache/avml/musl-cross-make-227df8b99103f9c59f6570babf892978e293082f
key: armv6b-build-chain-${{ runner.os }}-${{ hashFiles('eng/build-armv6b.sh') }}
key: armv6b-build-chain-${{ runner.os }}-${{ hashFiles('eng/build-armv6b.sh', 'eng/musl-cross-common.sh') }}
- name: build
run: |
eng/build-armv6b.sh
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@

*A portable volatile memory acquisition tool for Linux.*

AVML is an X86\_64 userland volatile memory acquisition tool written in
AVML is a Linux userland volatile memory acquisition tool written in
[Rust](https://www.rust-lang.org/), intended to be deployed as a static binary.
AVML can be used to acquire memory without knowing the target OS distribution
or kernel a priori. No on-target compilation or fingerprinting is needed.

Release builds are produced for x86-64, AArch64, ARMv7 little-endian hard-float,
and legacy ARMv6-compatible big-endian systems. ARMv7 acquisition currently
assumes 4096-byte memory pages.

## Features
* Save recorded images to external locations via Azure Blob Store or HTTP PUT
* Azure Blob Storage uploads retry transient failures via the Azure SDK's default exponential backoff policy (8 attempts, capped at one minute total elapsed).
Expand Down Expand Up @@ -197,6 +201,12 @@ Run `avml <COMMAND> --help` for per-command options.
# Build without upload functionality
cargo build --release --target x86_64-unknown-linux-musl --no-default-features

ARMv7 hard-float artifacts require an ARM musl cross-toolchain. The repository
build script creates the pinned toolchain and builds both full and minimal
static binaries:

eng/build-armv7.sh

# Testing on Azure

The testing scripts will create, use, and cleanup a number of resource groups, virtual machines, and a storage account.
Expand Down
49 changes: 4 additions & 45 deletions eng/build-armv6b.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ set -euo pipefail

cd "$(dirname "${BASH_SOURCE[0]}")/../"

source eng/musl-cross-common.sh

VERBOSE="${VERBOSE:-0}"

TARGET_NAME="${TARGET_NAME:-armv5te-unknown-linux-musleabi}"
Expand All @@ -17,10 +19,6 @@ ARTIFACT_DIR="${ARTIFACT_DIR:-target/armeb-unknown-linux-musleabi/release}"
CARGO_TOOLCHAIN="${CARGO_TOOLCHAIN:-nightly}"
CROSS_COMPILE="${CROSS_COMPILE:-armeb-linux-musleabi-}"
MUSL_TOOLCHAIN_DIR="${MUSL_TOOLCHAIN_DIR:-${HOME}/.cache/avml/armeb-linux-musleabi-cross}"
MUSL_CROSS_MAKE_REV="${MUSL_CROSS_MAKE_REV:-227df8b99103f9c59f6570babf892978e293082f}"
MUSL_CROSS_MAKE_URL="${MUSL_CROSS_MAKE_URL:-https://github.com/richfelker/musl-cross-make/archive/${MUSL_CROSS_MAKE_REV}.tar.gz}"
MUSL_CROSS_MAKE_SHA256="${MUSL_CROSS_MAKE_SHA256:-bb3fc7851088e1e5e1274ee56a0ab6ae176043d160fdf0b71027934b091f208a}"
MUSL_CROSS_MAKE_ARCHIVE="${MUSL_CROSS_MAKE_ARCHIVE:-}"
MUSL_CROSS_MAKE_DIR="${MUSL_CROSS_MAKE_DIR:-${HOME}/.cache/avml/musl-cross-make-${MUSL_CROSS_MAKE_REV}}"
BUILD_TOOLS_DIR="${BUILD_TOOLS_DIR:-target/armv6b-build-tools}"
if [[ "$BUILD_TOOLS_DIR" != /* ]]; then
Expand All @@ -29,7 +27,6 @@ fi
BUILD_LOG="${BUILD_LOG:-${BUILD_TOOLS_DIR}/build.log}"
TARGET_ENV="${TARGET_NAME//-/_}"
TARGET_ENV="${TARGET_ENV^^}"
MUSL_CROSS_MAKE_DL_CMD="curl --fail --show-error --location --continue-at - --output"

mkdir -p "$(dirname "$BUILD_LOG")"
: > "$BUILD_LOG"
Expand Down Expand Up @@ -105,47 +102,8 @@ ensure_rust_toolchain() {
rustup toolchain install "$CARGO_TOOLCHAIN" --profile minimal --component rust-src
}

verify_sha256() {
local archive="$1"
local expected_sha256="$2"

if ! echo "${expected_sha256} ${archive}" | sha256sum --check --status; then
echo "${archive}: SHA-256 verification failed; expected ${expected_sha256}" >&2
exit 1
fi
}

download_verified() {
local url="$1"
local archive="$2"
local expected_sha256="$3"

log "Downloading musl-cross-make"
curl --fail --show-error --location --retry 5 --retry-delay 10 \
--output "$archive" "$url"
verify_sha256 "$archive" "$expected_sha256"
}

ensure_source_musl_toolchain() {
local archive="${BUILD_TOOLS_DIR}/musl-cross-make-${MUSL_CROSS_MAKE_REV}.tar.gz"

mkdir -p "$BUILD_TOOLS_DIR" "$MUSL_CROSS_MAKE_DIR"
if [[ -n "$MUSL_CROSS_MAKE_ARCHIVE" ]]; then
archive="$MUSL_CROSS_MAKE_ARCHIVE"
verify_sha256 "$archive" "$MUSL_CROSS_MAKE_SHA256"
elif [[ ! -f "$archive" ]]; then
download_verified "$MUSL_CROSS_MAKE_URL" "$archive" "$MUSL_CROSS_MAKE_SHA256"
else
verify_sha256 "$archive" "$MUSL_CROSS_MAKE_SHA256"
fi

if [[ ! -f "${MUSL_CROSS_MAKE_DIR}/Makefile" ]]; then
log "Extracting musl-cross-make"
rm -rf "$MUSL_CROSS_MAKE_DIR"
mkdir -p "$MUSL_CROSS_MAKE_DIR"
tar --extract --gzip --directory "$MUSL_CROSS_MAKE_DIR" --strip-components=1 \
--file "$archive"
fi
avml_ensure_musl_cross_make_source "$BUILD_TOOLS_DIR" "$MUSL_CROSS_MAKE_DIR"

cat > "${MUSL_CROSS_MAKE_DIR}/config.mak" <<EOF
TARGET = armeb-linux-musleabi
Expand All @@ -158,6 +116,7 @@ GCC_CONFIG += --disable-libquadmath --disable-decimal-float --disable-libitm
GCC_CONFIG += --disable-fixed-point --disable-lto --enable-languages=c,c++
EOF

avml_ensure_musl_source "$MUSL_CROSS_MAKE_DIR"
log "Building musl cross toolchain"
make -C "$MUSL_CROSS_MAKE_DIR"
make -C "$MUSL_CROSS_MAKE_DIR" install
Expand Down
140 changes: 140 additions & 0 deletions eng/build-armv7.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#!/usr/bin/bash
#
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
#

set -euo pipefail

cd "$(dirname "${BASH_SOURCE[0]}")/../"

source eng/musl-cross-common.sh

TARGET_NAME="${TARGET_NAME:-armv7-unknown-linux-musleabihf}"
CROSS_COMPILE="${CROSS_COMPILE:-arm-linux-musleabihf-}"
MUSL_TOOLCHAIN_DIR="${MUSL_TOOLCHAIN_DIR:-${HOME}/.cache/avml/arm-linux-musleabihf-cross}"
MUSL_CROSS_MAKE_DIR="${MUSL_CROSS_MAKE_DIR:-${HOME}/.cache/avml/musl-cross-make-armv7-${MUSL_CROSS_MAKE_REV}}"
BUILD_TOOLS_DIR="${BUILD_TOOLS_DIR:-target/armv7-build-tools}"
TARGET_ENV="${TARGET_NAME//-/_}"
TARGET_ENV="${TARGET_ENV^^}"

log() {
echo "==> $*"
}

require_command() {
local command_name="$1"

if ! command -v "$command_name" >/dev/null 2>&1; then
echo "Missing required command: $command_name" >&2
exit 1
fi
}

install_apt_packages() {
local missing_packages=("$@")

if [[ "${#missing_packages[@]}" -eq 0 ]]; then
return
fi

require_command sudo
log "Installing missing host packages: ${missing_packages[*]}"
sudo apt-get update
sudo apt-get install --no-install-recommends --yes "${missing_packages[@]}"
}

ensure_host_tools() {
local packages=()

command -v curl >/dev/null 2>&1 || packages+=("curl")
command -v make >/dev/null 2>&1 || packages+=("make")
command -v perl >/dev/null 2>&1 || packages+=("perl")
command -v sha256sum >/dev/null 2>&1 || packages+=("coreutils")
command -v gcc >/dev/null 2>&1 || packages+=("build-essential")
command -v bzip2 >/dev/null 2>&1 || packages+=("bzip2")
command -v xz >/dev/null 2>&1 || packages+=("xz-utils")

if [[ "${#packages[@]}" -gt 0 ]]; then
if command -v apt-get >/dev/null 2>&1; then
install_apt_packages "${packages[@]}"
else
echo "Install these host tools before running $0: ${packages[*]}" >&2
exit 1
fi
fi
}

ensure_musl_toolchain() {
if command -v "${CROSS_COMPILE}gcc" >/dev/null 2>&1; then
return
fi
if [[ -x "${MUSL_TOOLCHAIN_DIR}/bin/arm-linux-musleabihf-gcc" ]]; then
CROSS_COMPILE="${MUSL_TOOLCHAIN_DIR}/bin/arm-linux-musleabihf-"
return
fi

avml_ensure_musl_cross_make_source "$BUILD_TOOLS_DIR" "$MUSL_CROSS_MAKE_DIR"

cat > "${MUSL_CROSS_MAKE_DIR}/config.mak" <<EOF
TARGET = arm-linux-musleabihf
OUTPUT = ${MUSL_TOOLCHAIN_DIR}
DL_CMD = ${MUSL_CROSS_MAKE_DL_CMD}
COMMON_CONFIG += CFLAGS="-g0 -Os" CXXFLAGS="-g0 -Os" LDFLAGS="-s"
COMMON_CONFIG += --disable-nls --with-debug-prefix-map=\$(CURDIR)=
GCC_CONFIG += --with-arch=armv7-a --with-fpu=vfpv3-d16 --with-float=hard
GCC_CONFIG += --disable-libquadmath --disable-decimal-float --disable-libitm
GCC_CONFIG += --disable-lto --enable-languages=c,c++
EOF

avml_ensure_musl_source "$MUSL_CROSS_MAKE_DIR"
log "Building ARMv7 musl cross toolchain"
make -C "$MUSL_CROSS_MAKE_DIR"
make -C "$MUSL_CROSS_MAKE_DIR" install
CROSS_COMPILE="${MUSL_TOOLCHAIN_DIR}/bin/arm-linux-musleabihf-"
}

verify_binary() {
local binary="$1"
local readelf="${CROSS_COMPILE}readelf"

"$readelf" -h "$binary" | grep -q "Class:.*ELF32"
"$readelf" -h "$binary" | grep -q "Data:.*little endian"
"$readelf" -h "$binary" | grep -q "Machine:.*ARM"
"$readelf" -h "$binary" | grep -q "Flags:.*hard-float ABI"
"$readelf" -A "$binary" | grep -q "Tag_CPU_arch: v7"
! "$readelf" -l "$binary" | grep -q "INTERP"
! "$readelf" -d "$binary" | grep -q "(NEEDED)"
}

ensure_host_tools
require_command rustup
log "Ensuring Rust target ${TARGET_NAME} is installed"
rustup +stable target add "$TARGET_NAME"
ensure_musl_toolchain

LINKER="${CROSS_COMPILE}gcc"
STRIP="${CROSS_COMPILE}strip"
TARGET_VARIABLE="${TARGET_NAME//-/_}"

export "CARGO_TARGET_${TARGET_ENV}_LINKER=${LINKER}"
export "CC_${TARGET_VARIABLE}=${LINKER}"
export "AR_${TARGET_VARIABLE}=${CROSS_COMPILE}ar"
export "RANLIB_${TARGET_VARIABLE}=${CROSS_COMPILE}ranlib"
export "CFLAGS_${TARGET_VARIABLE}=-march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=hard"
export OPENSSL_STATIC="${OPENSSL_STATIC:-1}"

log "Building minimal AVML for ${TARGET_NAME}"
cargo +stable build --release --no-default-features --target "$TARGET_NAME" --locked
cp "target/${TARGET_NAME}/release/avml" "target/${TARGET_NAME}/release/avml-minimal"

log "Building default AVML for ${TARGET_NAME}"
cargo +stable build --release --target "$TARGET_NAME" --locked

for binary in \
"target/${TARGET_NAME}/release/avml" \
"target/${TARGET_NAME}/release/avml-minimal"; do
log "Verifying ${binary}"
"$STRIP" "$binary"
verify_binary "$binary"
done
83 changes: 83 additions & 0 deletions eng/musl-cross-common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/bash
#
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
#

: "${MUSL_CROSS_MAKE_REV:=227df8b99103f9c59f6570babf892978e293082f}"
: "${MUSL_CROSS_MAKE_URL:=https://github.com/richfelker/musl-cross-make/archive/${MUSL_CROSS_MAKE_REV}.tar.gz}"
: "${MUSL_CROSS_MAKE_SHA256:=bb3fc7851088e1e5e1274ee56a0ab6ae176043d160fdf0b71027934b091f208a}"
: "${MUSL_CROSS_MAKE_ARCHIVE:=}"
: "${MUSL_VERSION:=1.2.6}"
: "${MUSL_SOURCE_URL:=https://sources.buildroot.net/musl/musl-${MUSL_VERSION}.tar.gz}"
: "${MUSL_SOURCE_SHA256:=d585fd3b613c66151fc3249e8ed44f77020cb5e6c1e635a616d3f9f82460512a}"
: "${MUSL_CROSS_MAKE_DL_CMD:=curl --fail --show-error --location --continue-at - --retry 5 --retry-delay 10 --output}"

avml_verify_sha256() {
local archive="$1"
local expected_sha256="$2"

if ! echo "${expected_sha256} ${archive}" | sha256sum --check --status; then
echo "${archive}: SHA-256 verification failed; expected ${expected_sha256}" >&2
exit 1
fi
}

avml_download_verified() {
local name="$1"
local url="$2"
local archive="$3"
local expected_sha256="$4"
local temporary_archive="${archive}.download"

log "Downloading ${name}"
curl --fail --show-error --location --retry 5 --retry-delay 10 \
--output "$temporary_archive" "$url"
avml_verify_sha256 "$temporary_archive" "$expected_sha256"
mv "$temporary_archive" "$archive"
}

avml_ensure_musl_cross_make_source() {
local build_tools_dir="$1"
local source_dir="$2"
local archive="${build_tools_dir}/musl-cross-make-${MUSL_CROSS_MAKE_REV}.tar.gz"

mkdir -p "$build_tools_dir" "$source_dir"
if [[ -n "$MUSL_CROSS_MAKE_ARCHIVE" ]]; then
archive="$MUSL_CROSS_MAKE_ARCHIVE"
avml_verify_sha256 "$archive" "$MUSL_CROSS_MAKE_SHA256"
elif [[ -f "$archive" ]]; then
avml_verify_sha256 "$archive" "$MUSL_CROSS_MAKE_SHA256"
else
avml_download_verified \
"musl-cross-make" \
"$MUSL_CROSS_MAKE_URL" \
"$archive" \
"$MUSL_CROSS_MAKE_SHA256"
fi

if [[ ! -f "${source_dir}/Makefile" ]]; then
log "Extracting musl-cross-make"
rm -rf "$source_dir"
mkdir -p "$source_dir"
tar --extract --gzip --directory "$source_dir" --strip-components=1 \
--file "$archive"
fi
}

avml_ensure_musl_source() {
local source_dir="${1}/sources"
local archive="${source_dir}/musl-${MUSL_VERSION}.tar.gz"

mkdir -p "$source_dir"
if [[ -f "$archive" ]]; then
avml_verify_sha256 "$archive" "$MUSL_SOURCE_SHA256"
return
fi

avml_download_verified \
"musl ${MUSL_VERSION}" \
"$MUSL_SOURCE_URL" \
"$archive" \
"$MUSL_SOURCE_SHA256"
}