contrib: add extract-handshakes kprobe example

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2018-02-28 00:17:43 +01:00
parent e6ce5fd386
commit d4421aea89
6 changed files with 176 additions and 0 deletions

1
.gitignore vendored
View file

@ -20,3 +20,4 @@ src/tests/qemu/distfiles/
*.nam *.nam
*.til *.til
*.pro.user *.pro.user
.cache.mk

3
contrib/extract-handshakes/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
offset-finder.o
offset-finder
offsets.include

View file

@ -0,0 +1,28 @@
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
CFLAGS ?= -O3 -march=native
CFLAGS += -Wall -pedantic -std=gnu11
offsets.include: offset-finder
./$^ > $@
offset-finder: offset-finder.c offset-finder.o
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^
offset-finder.o: offset-finder.c
$(MAKE) -C $(KERNELDIR) M=$(PWD) $@
objcopy -j '.rodata*' $@ $@
clean:
rm -f offset-finder offsets.include
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
.PHONY: clean
else
offset-finder-m := offset-finder.o
oldsrc := $(src)
src := $(src)/../../../src
include $(src)/compat/Kbuild.include
src := $(oldsrc)
endif

View file

@ -0,0 +1,20 @@
Handshake Extractor
===================
This will extract private keys from outgoing handshake sessions, prior
to them being sent, via kprobes. It exports the bare minimum to be
able to then decrypt all packets in the handshake and in the subsequent
transport data session.
Build:
$ make
Run (as root):
# ./extract-handshakes.sh
New handshake session:
LOCAL_STATIC_PRIVATE_KEY = QChaGDXeH3eQsbFAhueUNWFdq9KfpF3yl+eITjZbXEk=
REMOTE_STATIC_PUBLIC_KEY = HzgTY6aWXtuSyW/PUquZtg8LB/DyMwEXGkPiEmdSsUU=
LOCAL_EPHEMERAL_PRIVATE_KEY = UNGdRHuKDeqbFvmiV5FD4wP7a8PqI6v3Xnnz6Jc6NXQ=
PRESHARED_KEY = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

View file

@ -0,0 +1,80 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2015-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
# Copyright (C) 2017-2018 Peter Wu <peter@lekensteyn.nl>. All Rights Reserved.
set -e
ME_DIR="${BASH_SOURCE[0]}"
ME_DIR="${ME_DIR%/*}"
source "$ME_DIR/offsets.include" || { echo "Did you forget to run make?" >&2; exit 1; }
case "$(uname -m)" in
x86_64) ARGUMENT_REGISTER="%si" ;;
i386|i686) ARGUMENT_REGISTER="%dx" ;;
aarch64) ARGUMENT_REGISTER="%x1" ;;
arm) ARGUMENT_REGISTER="%r1" ;;
*) echo "ERROR: Unknown architecture" >&2; exit 1 ;;
esac
ARGS=( )
REGEX=".*: idxadd: .*"
for key in "${!OFFSETS[@]}"; do
values="${OFFSETS[$key]}"
values=( ${values//,/ } )
for i in {0..3}; do
value="$ARGUMENT_REGISTER"
for indirection in "${values[@]:1}"; do
value="+$indirection($value)"
done
value="+$((i * 8 + values[0]))($value)"
ARGS+=( "${key,,}$i=$value:x64" )
REGEX="$REGEX ${key,,}$i=0x([0-9a-f]+)"
done
done
turn_off() {
set +e
[[ -f /sys/kernel/debug/tracing/events/wireguard/idxadd/enable ]] || exit
echo 0 > /sys/kernel/debug/tracing/events/wireguard/idxadd/enable
echo "-:wireguard/idxadd" >> /sys/kernel/debug/tracing/kprobe_events
exit
}
trap turn_off INT TERM EXIT
echo "p:wireguard/idxadd index_hashtable_insert ${ARGS[*]}" >> /sys/kernel/debug/tracing/kprobe_events
echo 1 > /sys/kernel/debug/tracing/events/wireguard/idxadd/enable
unpack_u64() {
local i expanded="$1"
if [[ $ENDIAN == big ]]; then
printf -v expanded "%.*s$expanded" $((16 - ${#expanded})) 0000000000000000
for i in {0..7}; do
echo -n "\\x${expanded:(i * 2):2}"
done
elif [[ $ENDIAN == little ]]; then
(( ${#expanded} % 2 == 1 )) && expanded="0$expanded"
expanded="${expanded}0000000000000000"
for i in {0..7}; do
echo -n "\\x${expanded:((7 - i) * 2):2}"
done
else
echo "ERROR: Unable to determine endian" >&2
exit 1
fi
}
while read -r line; do
[[ $line =~ $REGEX ]] || continue
echo "New handshake session:"
j=1
for key in "${!OFFSETS[@]}"; do
bytes=""
for i in {0..3}; do
bytes="$bytes$(unpack_u64 "${BASH_REMATCH[j]}")"
((++j))
done
echo " $key = $(printf "$bytes" | base64)"
done
done < /sys/kernel/debug/tracing/trace_pipe

View file

@ -0,0 +1,44 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2015-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
struct def {
const char *name;
unsigned long offset;
unsigned long indirection_offset;
};
extern const struct def defs[];
#ifdef __KERNEL__
#include "../../../src/noise.h"
const struct def defs[] = {
{ "LOCAL_STATIC_PRIVATE_KEY", offsetof(struct noise_static_identity, static_private), offsetof(struct noise_handshake, static_identity) },
{ "LOCAL_EPHEMERAL_PRIVATE_KEY", offsetof(struct noise_handshake, ephemeral_private), -1 },
{ "REMOTE_STATIC_PUBLIC_KEY", offsetof(struct noise_handshake, remote_static), -1 },
{ "PRESHARED_KEY", offsetof(struct noise_handshake, preshared_key), -1 },
{ NULL, 0 }
};
#else
#include <stdio.h>
int main(int argc, char *argv[])
{
puts("declare -A OFFSETS=(");
for (const struct def *def = defs; def->name; ++def) {
printf("\t[%s]=%ld", def->name, def->offset);
if (def->indirection_offset != -1)
printf(",%ld", def->indirection_offset);
putchar('\n');
}
puts(")");
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
puts("ENDIAN=big");
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
puts("ENDIAN=little");
#else
#error "Unsupported endianness"
#endif
return 0;
}
#endif