|
@@ -0,0 +1,142 @@
|
|
|
|
|
+#!/usr/bin/python
|
|
|
|
|
+#
|
|
|
|
|
+# Copyright (c) 2019 Clementine Computing LLC.
|
|
|
|
|
+#
|
|
|
|
|
+# This file is part of PopuFare.
|
|
|
|
|
+#
|
|
|
|
|
+# PopuFare is free software: you can redistribute it and/or modify
|
|
|
|
|
+# it under the terms of the GNU Affero General Public License as published by
|
|
|
|
|
+# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
+# (at your option) any later version.
|
|
|
|
|
+#
|
|
|
|
|
+# PopuFare is distributed in the hope that it will be useful,
|
|
|
|
|
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
+# GNU Affero General Public License for more details.
|
|
|
|
|
+#
|
|
|
|
|
+# You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
+# along with PopuFare. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
+#
|
|
|
|
|
+
|
|
|
|
|
+# The output format is `<bit-length>|<payload>`, where the
|
|
|
|
|
+# <bit-length> and <payload> field are in ascii hex.
|
|
|
|
|
+#
|
|
|
|
|
+# Input is expected to come from stdin and as the result of the
|
|
|
|
|
+# `proxmark3` LUA script setup for low frequency read.
|
|
|
|
|
+# Input text is seached for a matching prefix of:
|
|
|
|
|
+#
|
|
|
|
|
+# #db# TAG ID:
|
|
|
|
|
+#
|
|
|
|
|
+# Once found, the ascii hex string is read, it's converted to binary
|
|
|
|
|
+# and parsed.
|
|
|
|
|
+# The first run of two '1' bits is removed from the 'left'.
|
|
|
|
|
+# The remaing number of bits is the bit length and the remaining
|
|
|
|
|
+# bit string is converted back to ascii.
|
|
|
|
|
+# Note the 'paylaod' can be a non exact multiple of 4 or 2.
|
|
|
|
|
+#
|
|
|
|
|
+# For example, if the following string was found:
|
|
|
|
|
+#
|
|
|
|
|
+# #db# TAG ID: 200712054a (677)
|
|
|
|
|
+#
|
|
|
|
|
+# This is converted to:
|
|
|
|
|
+#
|
|
|
|
|
+# 2 0 0 7 1 2 0 5 4 a
|
|
|
|
|
+# 0010 0000 0000 01 11 0001 0010 0000 0101 0100 1010
|
|
|
|
|
+# | |
|
|
|
|
|
+# -----------------------------------
|
|
|
|
|
+# 26 bits
|
|
|
|
|
+#
|
|
|
|
|
+# To evenually give the final result:
|
|
|
|
|
+#
|
|
|
|
|
+# 23|43F6311B2
|
|
|
|
|
+#
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+import os
|
|
|
|
|
+import re
|
|
|
|
|
+import sys
|
|
|
|
|
+import time
|
|
|
|
|
+
|
|
|
|
|
+rate_limit_ms = 3000.0
|
|
|
|
|
+
|
|
|
|
|
+def convert_tagid(s):
|
|
|
|
|
+ binstr = ""
|
|
|
|
|
+ for c in s:
|
|
|
|
|
+ v = int(c, 16)
|
|
|
|
|
+
|
|
|
|
|
+ _b = bin(int(c, 16))[2:]
|
|
|
|
|
+
|
|
|
|
|
+ __b = "0"*(4-len(_b)) + _b
|
|
|
|
|
+ binstr = binstr + __b
|
|
|
|
|
+
|
|
|
|
|
+ #print("c:", c, "v:", v, "_b:", _b, "binstr:", binstr)
|
|
|
|
|
+
|
|
|
|
|
+ #print("binstr:", binstr)
|
|
|
|
|
+
|
|
|
|
|
+ f, s, = -1, -1
|
|
|
|
|
+ for idx, b in enumerate(binstr):
|
|
|
|
|
+ if (b == '1') and (f < 0):
|
|
|
|
|
+ f = idx
|
|
|
|
|
+ elif (b == '1') and (s < 0):
|
|
|
|
|
+ s = idx
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ #print("first:", f, "second:", s)
|
|
|
|
|
+
|
|
|
|
|
+ filt_binstr = binstr[s+1:]
|
|
|
|
|
+ n = len(filt_binstr)
|
|
|
|
|
+ bitlen = n
|
|
|
|
|
+ fin_hex = ""
|
|
|
|
|
+ while n >= 4:
|
|
|
|
|
+
|
|
|
|
|
+ _b = filt_binstr[n-4:n]
|
|
|
|
|
+ fin_hex = hex(int(_b, 2))[2:] + fin_hex
|
|
|
|
|
+
|
|
|
|
|
+ #print("n:", n, "_b:", _b, "fin_hex:", fin_hex)
|
|
|
|
|
+
|
|
|
|
|
+ n -= 4
|
|
|
|
|
+ if n>0:
|
|
|
|
|
+ _b = filt_binstr[0:n]
|
|
|
|
|
+ fin_hex = hex(int(_b, 2))[2:] + fin_hex
|
|
|
|
|
+
|
|
|
|
|
+ #print("n:", n, "_b:", _b, "fin_hex:", fin_hex)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ #print("bitlen:", bitlen)
|
|
|
|
|
+ #print("binstr:", binstr)
|
|
|
|
|
+ #print("fin_hex:", fin_hex)
|
|
|
|
|
+
|
|
|
|
|
+ return hex(bitlen)[2:] + "|" + fin_hex
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+t_prv = time.time()*1000.0
|
|
|
|
|
+t_now = t_prv
|
|
|
|
|
+
|
|
|
|
|
+last_tok = ""
|
|
|
|
|
+
|
|
|
|
|
+while True:
|
|
|
|
|
+ line = sys.stdin.readline()
|
|
|
|
|
+ line = line.strip()
|
|
|
|
|
+
|
|
|
|
|
+ m = re.match("#db# TAG ID: ([^ ]*)", line)
|
|
|
|
|
+ if m:
|
|
|
|
|
+ #print("match:", m.group(1))
|
|
|
|
|
+
|
|
|
|
|
+ t_now = time.time()*1000.0
|
|
|
|
|
+
|
|
|
|
|
+ tok = convert_tagid(m.group(1)).upper()
|
|
|
|
|
+ if last_tok != tok:
|
|
|
|
|
+ print(tok)
|
|
|
|
|
+ sys.stdout.flush()
|
|
|
|
|
+ t_prv = t_now
|
|
|
|
|
+ last_tok = tok
|
|
|
|
|
+ elif (t_now - t_prv) >= rate_limit_ms:
|
|
|
|
|
+ print(tok)
|
|
|
|
|
+ sys.stdout.flush()
|
|
|
|
|
+ t_prv = t_now
|
|
|
|
|
+ last_tok = tok
|
|
|
|
|
+ else:
|
|
|
|
|
+ #print("#ignore")
|
|
|
|
|
+ pass
|
|
|
|
|
+
|
|
|
|
|
+
|