Last active
January 28, 2017 15:42
-
-
Save shajra/afed560778a556e487b201b5b4c2ad52 to your computer and use it in GitHub Desktop.
Nix work to replace strings in a binary
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
source "$stdenv/setup" | |
mkdir -p "$out/bin" | |
cp "$src/strings-replace" "$out/bin" | |
wrapProgram "$out/bin/strings-replace" \ | |
--prefix PATH : "${binutils}/bin" \ | |
--prefix PATH : "${coreutils}/bin" \ | |
--prefix PATH : "${gnugrep}/bin" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ stdenv | |
, binutils | |
, coreutils | |
, gnugrep | |
, makeWrapper | |
}: | |
stdenv.mkDerivation { | |
name = "strings-replace"; | |
buildInputs = [ makeWrapper ]; | |
src = ./.; | |
inherit binutils coreutils gnugrep; | |
builder = ./builder.sh; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh -eu | |
PROG_NAME="$0" | |
USAGE="$PROG_NAME [-h] PATTERN REPLACEMENT TARGET_BINARY_FILE | |
Replace references as detected by the 'strings' command in a binary file. | |
The replacement will be null terminated and overlaid on top of the | |
original reference (in-place). This means the replacement string can not | |
be longer than the string replaced, which this script checks for. | |
" | |
main() | |
( | |
while getopts h o | |
do | |
case $o in | |
h) echo "$USAGE"; exit 0;; | |
esac | |
done | |
shift $((OPTIND - 1)) | |
set +u | |
if [ -z "$1" -o -z "$2" -o -z "$3" ] | |
then echo "$USAGE"; exit 1 | |
fi | |
set -u | |
target="$1" | |
pattern="$2" | |
replacement="$3" | |
work "$target" "$pattern" "$replacement" | |
) | |
work() | |
( | |
target="$1" | |
pattern="$2" | |
replacement="$3" | |
replacement_file="$(make_replacement_file "$replacement")" | |
trap "rm $replacement_file" INT QUIT TSTP TERM EXIT | |
offsets="$(get_offsets "$target" "$pattern" "$replacement")" | |
for offset in $offsets | |
do replace "$replacement_file" "$offset" "$target" | |
done | |
) | |
get_offsets() | |
( | |
target="$1" | |
pattern="$2" | |
replacement="$3" | |
strings -t d "$target" \ | |
| grep -e "$pattern" \ | |
| while read -r line | |
do | |
offset="$(echo "$line" | cut -d ' ' -f 1)" | |
orig="$(echo "$line" | cut -d ' ' -f 2)" | |
if [ "${#orig}" -lt "${#replacement}" ] | |
then | |
msg="replacement too long" | |
msg="${msg}\n orig: ${orig}" | |
msg="${msg}\n new: ${replacement}" | |
fail "$msg" | |
fi | |
echo "$offset" | |
done | |
) | |
make_replacement_file() | |
( | |
replacement="$1" | |
tmpfile="$(mktemp)" | |
printf "$replacement\0" > "$tmpfile" | |
echo "$tmpfile" | |
) | |
replace() | |
( | |
replacement_file="$1" | |
offset="$2" | |
target="$3" | |
dd if="$replacement_file" of="$target" obs=1 seek="$offset" conv=notrunc | |
) | |
fail() | |
( | |
msg="$1" | |
echo "ERROR: $msg" > /dev/stderr | |
exit 1 | |
) | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I was statically linking a Haskell binary with Nix, because when you dynamically link, the transitive closure of all the dependencies in /nix/store can be very large.
However, because ekg uses Cabal "data-files", hard references to the ekg shared library in /nix/store get compiled into the resultant binary.
So I made another derivation of ekg that pruned away everything but the files needed at runtime. And I use the work above to replace
the reference to the original ekg in /nix/store with my new one: