Skip to content

Instantly share code, notes, and snippets.

@devurandom
Last active September 25, 2019 20:10
Show Gist options
  • Save devurandom/e5d7178e60c25248d3613871917a4f7c to your computer and use it in GitHub Desktop.
Save devurandom/e5d7178e60c25248d3613871917a4f7c to your computer and use it in GitHub Desktop.
Call scanelf --needed recursively to build a tree of library dependencies
#!/bin/bash
# This script avoids following security problem with `ldd` and thus `lddtree` by using `scanelf` instead:
# Be aware that in some circumstances (e.g., where the program specifies an ELF interpreter other than ld-linux.so), some
# versions of ldd may attempt to obtain the dependency information by attempting to directly execute the program (which may
# lead to the execution of whatever code is defined in the program's ELF interpreter, and perhaps to execution of the program
# itself). Thus, you should never employ ldd on an untrusted executable, since this may result in the execution of arbitrary
# code.
# Repeated appearances of a library are not fully resolved. The library is instead marked with `(*)` to indicate that its
# dependencies are omitted here, but can be found further up in the output.
die() {
[[ "$@" ]] && echo "$@"
exit 1
}
declare -A scanned
needed() {
local scan_lib="$1" level="${2:-0}" ws elfclass package
if ! [[ "${resolve_stdlib}" ]] ; then
case "${scan_lib}" in
*ld-linux*.so*|*libc.so*|*libdl.so*|*libm.so*|*libpthread.so*|*librt.so*|*libstdc++.so*|*libgcc_s.so*) return ;;
esac
fi
if ! [[ "${recursive}" ]] && [[ "${level}" -gt 1 ]] ; then
return
fi
for i in $(seq 1 "${level}") ; do
ws="${ws}| "
done
if [[ "${query_elfclass}" ]] && [[ "${level}" -eq 0 ]] ; then
elfclass="$(readelf -h "${scan_lib}" | grep 'Class:' | awk '{print $2}')"
fi
if [[ "${query_packages}" ]] ; then
package="$(q file -qv "${scan_lib}")"
fi
echo "${ws}${scan_lib}${elfclass:+ (${elfclass})}${package:+ (${package})}${scanned[${scan_lib}]:+ (*)}"
[[ "${scanned[${scan_lib}]}" ]] && return
scanned+=( ["${scan_lib}"]=1 )
while read lib ; do
needed "${lib}" $(("${level}" + 1))
done < <(scanelf --needed --use-ldpath "${scan_lib}" | awk '/^ET_(EXEC|DYN)/{print $2}' | tr , '\n')
}
recursive=yes
binary="$1"
[[ "${binary}" ]] || die "Usage $0 <binary>"
while [ $# -gt 0 ] ; do
arg=$1 ; shift
case $arg in
--resolve-stdlib) resolve_stdlib=yes ;;
--query-packages) query_packages=yes ;;
--query-elfclass) query_elfclass=yes ;;
--recursive=no) unset recursive ;;
esac
done
needed "${binary}"
@EoD
Copy link

EoD commented Nov 3, 2017

@devurandom Is there any reason this is in your gists instead of a normal repo? Do you have some more in-depth explanation why scanelf should be used over lddtree?

@devurandom
Copy link
Author

devurandom commented Sep 25, 2019

@devurandom Is there any reason this is in your gists instead of a normal repo? Do you have some more in-depth explanation why scanelf should be used over lddtree?

This script was written, because ldd has an arbitrary code execution security issue by design / specification. At the time I did not know about lddtree, so I wrote my own script.

Additionally it integrates with the Gentoo package database (via app-portage/portage-utils) and to show package information about the libraries found. It can also help to debug 32/64-bit issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment