Created
December 8, 2019 21:18
-
-
Save GermaniumSystem/eac00c5264900c6e889089631acce8fd to your computer and use it in GitHub Desktop.
A shell script for sending BTLE commands to the DEFCON Furs DC27 badge. Requires Bash, BlueZ, and not much else.
This file contains hidden or 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/bash | |
set -e | |
MAGIC_RAND_COLOR='7e' | |
MAGIC_AWOO='a0' | |
MAGIC_BEACON='00' | |
# The docs say MAGIC_EMOTE is b3. The docs are wrong. | |
MAGIC_EMOTE='b2' | |
MFG_FLAGS='ff ff 71' | |
DEV_NAME='BOGOBLE' | |
color="$MAGIC_RAND_COLOR" | |
iface='hci0' | |
sn='8678' | |
function printHelp { | |
cat <<EOHELP | |
BTLEComm.sh - Control nearby DEFCON Furs DC26 badges via BTLE beaconns. | |
Usage: | |
BTLEComm.sh [-i interface] [-s serial] <command> [argument] | |
Flags: | |
-c, --color COLOR Hex byte for the color using the badge's weird | |
format. Examples below. | |
00 = red | |
28 = green | |
50 = blue | |
78 = red | |
There are two special values. | |
7e = white | |
7f = random | |
-i, --interface IFACE The Bluetooth interface to broadcast from. | |
Defaults to $iface. | |
-s, --serial SERIALNUM The badge serial number to use. | |
Default to $sn. | |
-t, --time SECONDS Duration of the broadcase. Infinite by default. | |
Commands: | |
awoo - Starts a howl with a maximum TTL. | |
beacon - Broadcasts the same beacons that an idle badge would. | |
emote [face] - Causes nearby badges to display an emote. | |
Accepts an optional argument to specify 2 characters for | |
the face. Only uppercase letters, numbers, most special | |
characters, and lowercase "o" are supported. Examples below. | |
emote '^^' | |
emote '>>' | |
emote 'oO' | |
Special emotes such as 'owo' appear to have been removed. | |
EOHELP | |
exit | |
} | |
function decToHex { | |
printf "%02x" "$1" | |
} | |
function genFieldLength { | |
data="$1" | |
data_len="$(($(echo "$data" | sed 's/ //g' | wc -c - | sed 's/ .*//') / 2))" | |
data_hex_len="$(decToHex "$data_len")" | |
echo "$data_hex_len" | |
} | |
function setAdvertParams { | |
payload="$1" | |
mfg_len="$(genFieldLength "$MFG_FLAGS $payload")" | |
hex_name="$(asciiToHex "$DEV_NAME")" | |
name_len="$(($(genFieldLength "$hex_name") + 1))" | |
pkt="03 19 dc 27 02 01 06 $name_len 09 $hex_name $mfg_len $MFG_FLAGS $payload" | |
pkt_len="$(genFieldLength "$pkt")" | |
# https://gist.github.com/GermaniumSystem/d785ab9717dda672419740a40b0623bb | |
hcitool -i "$iface" cmd 0x08 0x0008 $pkt_len $pkt >/dev/null | |
} | |
function asciiToHex { | |
# This limits the input to 16 bytes, but no commands should be this long. | |
printf -- "$1" | od -t x1 | head -n 1 | sed -r 's/^[^ ]+ //' | |
} | |
function serialHex { | |
# The two bytes need to be flipped for the subsequent hcitool command. | |
printf "%04x\n" "$sn" | sed -r 's/(..)(..)/\2 \1/' | |
} | |
function stop_tx { | |
# Stop transmitting on ^C. | |
hciconfig "$iface" noleadv | |
echo -e "\nTransmission stopped." | |
} | |
# Sanity checks. | |
if [ -z "$(which hciconfig)" ] ; then | |
echo "Could not locate hciconfig! Does this system have BlueZ?" | |
exit 10 | |
fi | |
if [ -z "$1" ] ; then | |
printHelp | |
fi | |
if [ "$(id -u)" -ne "0" ] ; then | |
echo -e "\nWARNING: This script probably won't work when not run by root.\n" | |
fi | |
if ps ax | grep -v 'grep' | grep -qi 'bluetoothd' ; then | |
echo -e "\nWARNING: bluetoothd is running! This may break things.\n" | |
fi | |
# Parse args. | |
while [[ $# -gt 0 ]]; do | |
key=$1 | |
case $key in | |
-h|--help) | |
printHelp | |
;; | |
-c|--color) | |
color="$2" | |
shift | |
shift | |
;; | |
-i|--interface) | |
iface="$2" | |
shift | |
shift | |
;; | |
-s|--serial) | |
sn="$2" | |
shift | |
shift | |
;; | |
-t|--time) | |
txtime="$2" | |
shift | |
shift | |
;; | |
*) | |
cmd="$1" | |
if [ -n "$2" ] ; then | |
arg="$2" | |
shift | |
fi | |
shift | |
;; | |
esac | |
done | |
#echo "Generating payload..." | |
sn_bytes="$(serialHex)" | |
case $cmd in | |
awoo) | |
# Magic byte, two serial num bytes, TTL byte, two origin serial num bytes. | |
data="$MAGIC_AWOO $sn_bytes ff $sn_bytes" | |
;; | |
beacon) | |
# Magic byte, two serial num bytes. | |
data="$MAGIC_BEACON $sn_bytes" | |
;; | |
emote) | |
# Magic byte, optional color byte, 2 optional emote bytes. | |
data="$MAGIC_EMOTE" | |
if [ -n "$arg" ] ; then | |
arg_hex="$(asciiToHex "$arg")" | |
data="$MAGIC_EMOTE $color $arg_hex" | |
fi | |
echo "$data" | |
;; | |
*) | |
echo "Unknown command '$cmd'" | |
exit 2 | |
;; | |
esac | |
echo "Bringing interface $IFACE up..." | |
hciconfig "$IFACE" up | |
#echo "Setting advertisement parameters..." | |
setAdvertParams "$data" | |
#echo "Enabling advertisement..." | |
hciconfig "$IFACE" leadv 3 | |
trap stop_tx INT # Stop trasmitting if ^C is pressed. | |
if [ -n "$txtime" ] ; then | |
printf "Broadcasting! Stopping transmission in $txtime seconds." | |
for i in $(seq 1 "$txtime"); do | |
printf '.' | |
sleep 1s | |
done | |
stop_tx | |
else | |
printf "Broadcasting! Press ^C to stop transmission." | |
while true ; do | |
printf '.' | |
sleep 1s | |
done | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment