Okay, now that I have my first implant with blinkies, I cracked out out my trusty Proxmark3 and whipped up a small proof of concept code for the project I have in mind: using the blinkies to send out messages in morse code. I’m a ham, so it works for me. Check it out:
https://www.dailymotion.com/embed/video/x7vzpl2
The Proxmark is a bit sluggish to switch the field on and off, so it can’t go over 10 words per minute without the dits and dahs becoming unrecognizable. I’ll need to come up with my own hardware to reach the 40 wpm I want. Also, I think I have a very good chance of being able to light up the 3 blinkies individually with 3 coils. Obviously the Proxmark can’t do that. Still, it works
Here’s the code, for those who are interested - Linux only:
#!/usr/bin/python3
### Parameters
pm3_client = "/usr/local/bin/proxmark3"
pm3_default_dev = "/dev/ttyACM0"
default_wpm = 10 #words per minute
### Modules
import re
import os
import sys
import argparse
from time import sleep
from pty import openpty
from select import select
from subprocess import Popen, DEVNULL, PIPE
# Morse code dictionary
morsecode = {
"A": ".-",
"B": "-...",
"C": "-.-.",
"D": "-..",
"E": ".",
"F": "..-.",
"G": "--.",
"H": "....",
"I": "..",
"J": ".---",
"K": "-.-",
"L": ".-..",
"M": "--",
"N": "-.",
"O": "---",
"P": ".--.",
"Q": "--.-",
"R": ".-.",
"S": "...",
"T": "-",
"U": "..-",
"V": "...-",
"W": ".--",
"X": "-..-",
"Y": "-.--",
"Z": "--..",
"1": ".----",
"2": "..---",
"3": "...--",
"4": "....-",
"5": ".....",
"6": "-....",
"7": "--...",
"8": "---..",
"9": "----.",
"0": "-----",
"=": "-...-",
"/": "-..-.",
"?": "..--..",
",": "--..--",
".": ".-.-.-",
":": "---...",
"'": ".----.",
'"': ".-..-.",
"_": "..--.-",
"(": "-.--.",
")": "-.--.-",
"#": "-.---",
"-": "-....-",
"|": "...-..",
"\\": "-.....",
"*": "-----.",
";": "-.-.-.",
"@": ".--.-.",
"^": "....--.-.",
"$": "...-..-",
"!": "....-.",
">": "....---.",
"]": "....-....",
"[": "....-..",
"<": "....-.-..",
"&": "....--.",
"%": "....-.--.",
"~": "....--",
"+": ".-.-.",
"{": "....-.--",
"}": "....--..-",
"[AR]": ".-.-.",
"[AS]": ".-...",
"[BK]": "-...-.-",
"[BT]": "-...-",
"[KA]": "-.-.-",
"[CL]": "-.-..-..",
"[KN]": "-.--.",
"[VA]": "...-.-",
"[VE]": "...-.",
"[GR]": "--..-.",
"[HM]": "....--",
"[IX]": "..-..-",
"[IMI]": "..--..",
"[INT]": "..-.-",
"[SOS]": "...---..."}
### Main routine
def main():
"""Main routine
"""
# Read the command line arguments
argparser = argparse.ArgumentParser()
argparser.add_argument(
"-d", "--device",
type = str,
help = "Proxmark3 device file (default: {})".format(pm3_default_dev),
default = pm3_default_dev,
required = False
)
argparser.add_argument(
"-w", "--words-per-minute",
type = int,
help = "Rate of the morse code (default: {})".format(default_wpm),
default = default_wpm,
required = False
)
argparser.add_argument(
"message",
type = str,
help = "Text to output in morse code on LED implant",
)
args = argparser.parse_args()
dev = args.device
wpm = args.words_per_minute
msg = args.message.upper()
# Possible Proxmark3 console prompts
pm3_prompts_regex = re.compile("^(proxmark3>|\[.*\] pm3 -->)$")
# Proxmark3 "HF field on" and "HF field off" command
field_on = "hf 14a raw -a -p"
field_off = "hf 14a reader x s"
# Turn the message into a sequence of field-on durations (positive) and
# field-off durations (negative)
morsechars = []
i = 0
while i < len(msg):
if msg[i] == "[":
j = msg.find("]", i + 1)
if j > 0 and msg[i : j + 1] in morsecode:
morsechars.append(morsecode[msg[i : j + 1]])
i = j + 1
continue
if msg[i] == " " or msg[i] == "\t":
morsechars.append(" ")
elif msg[i] in morsecode:
morsechars.append(morsecode[msg[i]])
else:
print("Untranslatable in morse code: {} - dropped".format(msg[i]))
i += 1
ditlen = 1.2 / wpm
morseseq = []
for mc in morsechars:
if mc == " ":
if morseseq:
morseseq[-1] = -ditlen * 7
else:
morseseq.append(-ditlen * 7)
continue
for c in mc:
if c == ".":
morseseq.append(ditlen)
else:
morseseq.append(ditlen * 3)
morseseq.append(-ditlen)
morseseq[-1] = -ditlen * 3
if not morseseq:
print("Nothing to do")
return(0)
# Create a PTY pair to fool the Proxmark3 client into working interactively
pty_master, pty_slave = openpty()
# Spawn the Proxmark3 client
pm3_proc = Popen([pm3_client, dev], bufsize=0, env={},
stdin=pty_slave, stdout=PIPE, stderr=DEVNULL)
# Interact with the Proxmark3 client
recvbuf = ""
sendcmd = True
while True:
# Read lines from the Proxmark3 client
rlines = []
for c in pm3_proc.stdout.read(256).decode("ascii"):
if c == "\n" or c == "\r" or pm3_prompts_regex.match(recvbuf):
rlines.append(recvbuf)
recvbuf = ""
elif len(recvbuf)<256 and c.isprintable():
recvbuf += c
# Process the lines from the client
for l in rlines:
# If we detect a fatal error from the client, exit
if re.search("(proxmark failed|offline|OFFLINE|unknown command)", l):
return(-1)
# Do we have a prompt
if pm3_prompts_regex.match(l):
# If we're done sending out the sequence, instruct the Proxmark client
# to quit, wait on it and exit ourselves
if not morseseq:
os.write(pty_master, ("quit" + "\r").encode("ascii"))
pm3_proc.wait()
return(0)
# Get the next element to play in the sequence
duration = morseseq.pop(0)
# Flip the field as needed
os.write(pty_master, ((field_on if duration >=0 else field_off) \
+ "\r").encode("ascii"))
sleep(abs(duration))
### Jump to the main routine
if __name__ == "__main__":
sys.exit(main())