├── AMOOKScanner.py ├── AMOOKTransmit.py ├── DualRF.py ├── PWMScanner.py ├── README.md ├── RFJammer.py ├── RFSimpleReplay.py ├── RFSimpleTransmit.py └── decodeOOK.py /AMOOKScanner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | from rflib import * 5 | import operator 6 | import time 7 | import argparse 8 | 9 | 10 | parser = argparse.ArgumentParser(description='Application to use a RFCat compatible device to listen and calculate binary of a transmitted signal',version="0.1-bricktop") 11 | parser.add_argument('-f', action="store", default="433880000", dest="baseFreq",help='Target frequency to listen for remote (default 433880000)',type=int) 12 | parser.add_argument('-i', action="store", default="24000", dest="chanWidth",help='Width of each channel (lowest being 24000 -- default)',type=int) 13 | parser.add_argument('-b', action="store", dest="baudRate",default=4800,help='Baudrate, defaults to 4800',type=int) 14 | parser.add_argument('-vv', action="store_true", dest="verbose", default=False,help='Verbose output') 15 | results = parser.parse_args() 16 | 17 | 18 | d = RfCat() 19 | 20 | d.setMdmModulation(MOD_ASK_OOK) 21 | d.setFreq(results.baseFreq) 22 | d.makePktFLEN(0) 23 | d.setMdmDRate(results.baudRate) 24 | d.lowball(0) 25 | d.setModeRX() 26 | 27 | 28 | 29 | d.setMdmChanSpc(results.chanWidth) 30 | d.setChannel(0) 31 | 32 | lens = dict() 33 | allstrings = [] 34 | stored_codes = [] 35 | 36 | 37 | start = time.time() 38 | print "Starting scan..." 39 | while True: 40 | try: 41 | 42 | y, t = d.RFrecv(1) 43 | sampleString=y.encode('hex') 44 | 45 | zeroPadding = [match[0] for match in re.findall(r'((0)\2{25,})', sampleString)] 46 | for z in zeroPadding: 47 | currLen = len(z) 48 | if currLen in lens.keys(): 49 | lens[currLen] = lens[currLen] + 1 50 | else: 51 | lens[currLen] = 1 52 | sorted_lens = sorted(lens.items(), key=operator.itemgetter(1), reverse=True) 53 | lens = dict() 54 | if(sorted_lens and sorted_lens[0][0] > 0 and sorted_lens[0][0] < 400): 55 | zeroPaddingString = "0" * sorted_lens[0][0] 56 | 57 | 58 | possibleStrings = sampleString.split(zeroPaddingString) 59 | possibleStrings = [s.strip("0") for s in possibleStrings] 60 | if(results.verbose == True): 61 | print "Possible codes found (-vv):" 62 | print "---------------------------" 63 | print possibleStrings 64 | 65 | for s in possibleStrings: 66 | if(len(s) > 5): 67 | allstrings.append(s) 68 | 69 | if(results.verbose == True): 70 | print "Binary for codes found (-vv):" 71 | print "---------------------------" 72 | 73 | if(len(allstrings) > 0): 74 | lengths = [len(i) for i in allstrings] 75 | most_common_length = max(set(lengths), key=lengths.count) 76 | binaryKeys = [] 77 | for a in allstrings: 78 | if(len(a) == most_common_length): 79 | if(results.verbose == True): 80 | print str(bin(int(a,16))[2:]) 81 | binaryKeys.append(bin(int(a,16))[2:]) 82 | else: 83 | if(len(a) -1 == most_common_length): 84 | if(results.verbose == True): 85 | print str(bin(int(a,16))[2:-1]) 86 | binaryKeys.append(bin(int(a,16))[2:-1]) 87 | 88 | maxlen = len(max(binaryKeys, key=len)) 89 | 90 | for i in range(0,len(binaryKeys)): 91 | if(len(binaryKeys[i]) < maxlen): 92 | binaryKeys[i] = binaryKeys[i] + ("0" * (maxlen - len(binaryKeys[i]))) 93 | 94 | print "Possible Signals:" + str(len(allstrings)) 95 | finalKey = ""; 96 | for charPos in range(0,maxlen): 97 | total = 0; 98 | for i in range(0,len(binaryKeys)): 99 | thisChar = binaryKeys[i][charPos] 100 | total += int(thisChar) 101 | if(total > (len(binaryKeys) / 2)): 102 | finalKey += "1" 103 | else: 104 | finalKey += "0" 105 | print "Calculated Key (bin): " + finalKey 106 | print "--------" 107 | 108 | except ChipconUsbTimeoutException: 109 | pass 110 | except KeyboardInterrupt: 111 | d.setModeIDLE() 112 | print "bye." 113 | sys.exit() 114 | pass 115 | d.setModeIDLE() 116 | print "bye." 117 | sys.exit() 118 | 119 | 120 | -------------------------------------------------------------------------------- /AMOOKTransmit.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from rflib import * 3 | from struct import * 4 | import bitstring 5 | import argparse 6 | 7 | 8 | def makeKey(key,large = True): 9 | if(key[0:1] == "1"): 10 | key = "1" + key 11 | pwm_str_key = "" 12 | for k in key: 13 | x = "*" 14 | if(k == "0"): 15 | x = "1110" # A zero is encoded as a longer high pulse (high-high-low) 16 | if(k == "1"): 17 | x = "1100" # and a one is encoded as a shorter high pulse (high-low-low). 18 | if(k == "x"): 19 | x = "0000" # AndrewMac: Maybe the transmitter is slow to warm up to maximum power? What happens if you add some zero padding at the start? 20 | if(large == True): 21 | x = x + "0" 22 | pwm_str_key = pwm_str_key + x 23 | key_packed = bitstring.BitArray(bin=pwm_str_key).tobytes() 24 | return key_packed; 25 | 26 | def makeKeyFull(key,large = True): 27 | pwm_str_key = key 28 | key_packed = bitstring.BitArray(bin=pwm_str_key).tobytes() 29 | return key_packed; 30 | 31 | def sendKey(key,num): 32 | for i in range(0,num): 33 | d.RFxmit(key) 34 | 35 | parser = argparse.ArgumentParser(description='Application to use a RFCat compatible device to transmit a particular AM/OOK binary signal\n Use -b for a calculated(full) binary or -c for a compact binary (full will be calculated) ',version="0.1-bricktop") 36 | parser.add_argument('-f', action="store", default="433880000", dest="baseFreq",help='Target frequency to listen for remote (default 433880000)',type=int) 37 | parser.add_argument('-r', action="store", dest="baudRate",default=4800,help='Baudrate, defaults to 4800',type=int) 38 | group = parser.add_mutually_exclusive_group(required=True) 39 | group.add_argument('-b', action="store", dest="fullBin",default=False,help='Full binary value to Transmit') 40 | group.add_argument('-c', action="store", dest="compactBin", default=False,help='Compact binary value to Transmit') 41 | parser.add_argument('-t', action="store", dest="repeatTimes",default=15,help='Number of times to repeat the signal,defaults to 15',type=int) 42 | parser.add_argument('-vv', action="store_true", dest="verbose", default=False,help='Verbose output') 43 | results = parser.parse_args() 44 | 45 | freq = results.baseFreq 46 | baudRate = results.baudRate 47 | 48 | d = RfCat() 49 | 50 | d.setMdmModulation(MOD_ASK_OOK) 51 | d.setFreq(freq) 52 | d.setMdmSyncMode(0) 53 | d.setMdmDRate(baudRate) 54 | 55 | fullKey = '' 56 | if(results.compactBin is not False): 57 | fullKey = makeKey(results.compactBin,True) 58 | if(results.fullBin is not False): 59 | fullKey = makeKeyFull(results.fullBin,True) 60 | 61 | d.makePktFLEN(len(fullKey)) 62 | print "Transmitting..." 63 | sendKey(fullKey,results.repeatTimes) 64 | print "Done." 65 | d.setModeIDLE() 66 | -------------------------------------------------------------------------------- /DualRF.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import datetime as dt 5 | from rflib import * 6 | from struct import * 7 | import bitstring 8 | import operator 9 | import time 10 | import argparse 11 | 12 | 13 | APP_NIC = 0x42 14 | RFCAT_START_SPECAN = 0x40 15 | RFCAT_STOP_SPECAN = 0x41 16 | APP_SYSTEM = 0xff 17 | SYS_CMD_RFMODE = 0x88 18 | SPECAN_QUEUE = 1 19 | RFST_SRX = 0x02 20 | 21 | keyLen = 0 22 | baudRate = 4800 23 | leaveLoop = False; 24 | 25 | 26 | 27 | def startJam(rfcatInstance,rfcatjamFreq): 28 | rfcatInstance.setMaxPower() 29 | rfcatInstance.setRFRegister(PA_TABLE0, 0xFF) 30 | rfcatInstance.setRFRegister(PA_TABLE1, 0xFF) 31 | rfcatInstance.setFreq(rfcatjamFreq) 32 | rfcatInstance.setModeTX() 33 | print "[!] Jamming at " + str(rfcatjamFreq) 34 | 35 | def stopJam(rfcatInstance): 36 | rfcatInstance.setModeIDLE() 37 | rfcatInstance.setRFRegister(PA_TABLE0, 0x00) 38 | rfcatInstance.setRFRegister(PA_TABLE1, 0x00) 39 | 40 | def configure(rfcatInstance): 41 | rfcatInstance.setMdmModulation(MOD_ASK_OOK) 42 | rfcatInstance.setFreq(baseFreq) 43 | rfcatInstance.setMdmDRate(4800) 44 | 45 | parser = argparse.ArgumentParser(description='Application to use two RfCat compatible devices to scan for a signal and jam it',version="0.1-bricktop") 46 | parser.add_argument('-b', action="store", default="433000000", dest="baseFreq",help='Base frequency to scan from (default 433000000)',type=int) 47 | parser.add_argument('-o', action="store", default="100000", dest="jamOffset",help='Offset from frequency to jam (default 100000 hz)',type=int) 48 | parser.add_argument('-m', action="store", default="-10", dest="minStrength",help='Minimum signal strength to classify signal (default -10 dbm)',type=int) 49 | parser.add_argument('-c', action="store", default="100", dest="count",help='number of channels to scan (default 100)',type=int) 50 | parser.add_argument('-i', action="store", default="24000", dest="chanWidth",help='Width of each channel (lowest being 24000 -- default)',type=int) 51 | results = parser.parse_args() 52 | 53 | baseFreq = results.baseFreq 54 | jamDistance = results.jamOffset 55 | rssi_minimum_for_signal = results.minStrength; 56 | count = results.count 57 | inc = results.chanWidth 58 | 59 | 60 | print "Setting up ScanJam - configuring Scanner" 61 | print "----------------------------------------" 62 | print "(i) Start: " + str(baseFreq) 63 | print "(i) End: " +str((baseFreq + (count*inc))) 64 | print "(i) Min dBm: " +str(rssi_minimum_for_signal) 65 | 66 | d = RfCat(idx=0) 67 | configure(d) 68 | d.setModeRX() 69 | 70 | raw_input("Press Enter to setup Jammer") 71 | d2 = RfCat(idx=1) 72 | configure(d2) 73 | 74 | halfwayFreq = baseFreq + ((count * (inc/2)) / 2) 75 | 76 | 77 | start = time.time() 78 | startJam(d2,halfwayFreq); 79 | end = time.time() 80 | print end-start 81 | time.sleep(1) 82 | d2.setModeIDLE() 83 | 84 | raw_input("Press Enter to start scanning...") 85 | 86 | 87 | 88 | d.setMdmChanSpc(inc) 89 | freq, fbytes = d.getFreq() 90 | delta = d.getMdmChanSpc() 91 | 92 | 93 | d.setFreq(baseFreq) 94 | 95 | #print "[+] Scanning for signals from " + str(baseFreq) + " to " + str((baseFreq + (count*inc))) + " for anything stronger than " + str(rssi_minimum_for_signal) 96 | print "[+] Scanning for signals..." 97 | d.send(APP_NIC, RFCAT_START_SPECAN, "%c" % (count) ) 98 | jamming = False; 99 | try: 100 | strongestSignal = rssi_minimum_for_signal #this seemed so reasonable. 101 | highestFreq = 0 # not sure why i have to use this rather than strongest signal >_< 102 | 103 | while True: 104 | jamFreq = 0 105 | if(jamming == False): 106 | #start = time.time() 107 | rssi_values, timestamp = d.recv(APP_SPECAN, SPECAN_QUEUE, 10000) 108 | #print "." 109 | #end = time.time() 110 | rssiTime = end-start 111 | rssi_values = [ ((ord(x)^0x80)/2)-88 for x in rssi_values ] 112 | 113 | highestVal = max(rssi_values) 114 | if(highestVal >= strongestSignal): 115 | strongestSignal = highestVal 116 | jamFreq = baseFreq + (rssi_values.index(highestVal) * (inc/2)) 117 | startJam(d2,jamFreq + jamDistance) 118 | print rssi_values 119 | #end = time.time() 120 | #jamTime = end-start 121 | #print "rssitime:" + str(rssiTime) 122 | #print "jamTime:" + str(jamTime) 123 | #startJam(d2,433740000) 124 | #print "[+] Jamming from jammer rfcat ( d2 ) Target freq:" + str(jamFreq) 125 | if(jamming == False): 126 | d.send(APP_NIC, RFCAT_STOP_SPECAN, '') 127 | print "[-] Stopping scan on scanner rfcat ( d )" 128 | jamming = True 129 | 130 | 131 | 132 | 133 | 134 | except KeyboardInterrupt: 135 | print "bye." 136 | pass; 137 | 138 | 139 | print "end."; 140 | stopJam(d) 141 | stopJam(d2) 142 | d.setModeIDLE() 143 | d2.setModeIDLE() 144 | -------------------------------------------------------------------------------- /PWMScanner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import time 5 | from rflib import * 6 | from struct import * 7 | import argparse 8 | import bitstring 9 | import re 10 | import operator 11 | import numpy as np 12 | import matplotlib.pyplot as plt 13 | from collections import Counter 14 | import datetime as dt 15 | import select 16 | import tty 17 | import termios 18 | 19 | 20 | 21 | chr = 0 22 | keyLen = 0 23 | baudRate = 4800 24 | frequency = 433855000 25 | repeatNum = 25 26 | 27 | def ConfigureD(d): 28 | d.setMdmModulation(MOD_ASK_OOK) 29 | d.setFreq(frequency) 30 | d.makePktFLEN(keyLen) 31 | d.setMdmDRate(baudRate) 32 | #d.setMdmChanBW(1); 33 | #d.setMaxPower() 34 | d.lowball() 35 | 36 | if(results.verbose == True): 37 | print "[+] Radio Config:" 38 | print " [-] ---------------------------------" 39 | print " [-] MDMModulation: MOD_ASK_OOK" 40 | print " [-] Frequency: ",frequency 41 | print " [-] Packet Length:",keyLen 42 | print " [-] Baud Rate:",baudRate 43 | print "[-] ---------------------------------" 44 | 45 | print "" 46 | print "######################################################" 47 | print "# PWM Scanning with RFCat #" 48 | print "# #" 49 | print "# @AndrewMohawk #" 50 | print "# http://www.andrewmohawk.com #" 51 | print "# #" 52 | print "######################################################" 53 | print "" 54 | parser = argparse.ArgumentParser(description='Simple program to scan for PWM OOK codes',version="RFCat PWM Scanner 0.01 - by Andrew MacPherson (www.andrewmohawk.com) / @AndrewMohawk ") 55 | parser.add_argument('-fa', action="store", default="433000000", dest="startFreq",help='Frequency to start scan at, defaults to 433000000',type=long) 56 | parser.add_argument('-fb', action="store", default="434000000", dest="endFreq",help='Frequency to end scan at, defaults to 434000000',type=long) 57 | parser.add_argument('-fs', action="store", default="50000", dest="stepFreq",help='Frequency step for scanning, defaults to 50000',type=long) 58 | parser.add_argument('-ft', action="store", default="1000", dest="timeStepFreq",help='Frequency step delay, defaults to 1000 milliseconds',type=long) 59 | parser.add_argument('-vv', action="store_true", dest="verbose", default=False,help='Verbose output') 60 | parser.add_argument('-a',action="store_true", default=False,help='Automatically replay after collecting') 61 | parser.add_argument('-br', action="store", dest="baudRate",default=4800,help='Baudrate to transmit at, defaults to 4800',type=int) 62 | parser.add_argument('-r', action="store", dest="repeat", default=15,help='Amount of times to repeat when transmitting, defaults to 15',type=int) 63 | parser.add_argument('-p', action="store", dest="paddingZeros", default=15,help='Amount of repeated zeros to search for when looking for patterns',type=int) 64 | #parser.add_argument('-g',action="store_true",dest="showGraph", default=False,help='Show graph of data') 65 | parser.add_argument('-ms', action="store", dest="minimumStrength", default=-80,help='Minimum strength, defaults to -80',type=int) 66 | parser.add_argument('-ln', action="store", dest="lockNum", default=5,help='Minimum `codes` to receive before locking',type=int) 67 | parser.add_argument('-rp', action="store_true", dest="replayKey", default=True,help='Replay most common code automatically, default true') 68 | results = parser.parse_args() 69 | 70 | currFreq = results.startFreq; 71 | repeatNum = results.repeat 72 | frequency = currFreq 73 | sys.stdout.write("Configuring RFCat...\n") 74 | d = RfCat() 75 | ConfigureD(d) 76 | allstrings = {} 77 | lens = dict() 78 | lockOnSignal = True 79 | lockedFreq = False 80 | 81 | ''' 82 | if (results.showGraph == True): 83 | x = range(0,38) 84 | y = range(0,38) 85 | line, = plt.plot(x,y,"-") 86 | plt.ion() 87 | plt.show() 88 | plt.draw() 89 | x = range(0,38) 90 | line.set_xdata(x) 91 | ''' 92 | def spinning_cursor(): 93 | while True: 94 | for cursor in '|/-\\': 95 | yield cursor 96 | 97 | spinner = spinning_cursor() 98 | BOLD = '\033[1;37;40m' 99 | ENDC = '\033[0m' 100 | RED = '\033[1;31;40m' 101 | BLUE = '\033[1;34;40m' 102 | GREEN = '\033[1;32;40m' 103 | YELLOW = '\033[1;33;40m' 104 | WHITE = '\033[1;37;40m' 105 | LIGHTBLUE = '\033[1;36;40m' 106 | 107 | 108 | 109 | print "Scanning for AM/OOK Remotes... Press " + BOLD + WHITE + "" + ENDC + " to stop and " + BOLD + WHITE + " any key" + ENDC + " to continue\n" 110 | 111 | def isData(): 112 | return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []) 113 | 114 | old_settings = termios.tcgetattr(sys.stdin) 115 | tty.setcbreak(sys.stdin.fileno()) 116 | 117 | def showStatus(): 118 | sys.stdout.write('\r' + BOLD + ENDC + "[ " + BOLD + YELLOW) 119 | sys.stdout.write(spinner.next()) 120 | strength= 0 - ord(str(d.getRSSI())) 121 | sigFound = "0" 122 | if(currFreq in allstrings): 123 | sigFound = str(len(allstrings[currFreq])) 124 | sys.stdout.write(ENDC + ' ] Freq: [ ' + LIGHTBLUE + str(currFreq) + ENDC + ' ] Strength [ ' + YELLOW + str(strength) + ENDC + ' ] Signals Found: [ ' + GREEN + sigFound + ENDC + " ]" ) 125 | if(lockedFreq == True): 126 | sys.stdout.write(ENDC + RED + " [!FREQ LOCKED!]" + ENDC) 127 | #else: 128 | # sys.stdout.write(" " * 30) 129 | #sys.stdout.write(" " * 10) 130 | #yes, i know, icky! 131 | sys.stdout.flush() 132 | #sys.stdout.write("\n- Press Any Key to End Scan -"); 133 | 134 | n1=dt.datetime.now() 135 | 136 | 137 | 138 | while True: 139 | try: 140 | if isData(): 141 | x= ord(sys.stdin.read(1)) 142 | if (x == 3 or x == 10): 143 | break 144 | elif(x == 32): 145 | print "unlocking"; 146 | currFreq += results.stepFreq 147 | lockedFreq = False 148 | y, t = d.RFrecv(1) 149 | sampleString=y.encode('hex') 150 | # lets find all the zero's 151 | showStatus(); 152 | #print "Received: %s" % (y.encode('hex')) 153 | zeroPadding = [match[0] for match in re.findall(r'((0)\2{25,})', sampleString)] 154 | for z in zeroPadding: 155 | currLen = len(z) 156 | if currLen in lens.keys(): 157 | lens[currLen] = lens[currLen] + 1 158 | else: 159 | lens[currLen] = 1 160 | sorted_lens = sorted(lens.items(), key=operator.itemgetter(1), reverse=True) 161 | lens = dict() 162 | if(sorted_lens and sorted_lens[0][0] > 0 and sorted_lens[0][0] < 400): 163 | zeroPaddingString = "0" * sorted_lens[0][0] 164 | #print "zeros used in padding: " , zeroPaddingString 165 | 166 | possibleStrings = sampleString.split(zeroPaddingString) 167 | possibleStrings = [s.strip("0") for s in possibleStrings] 168 | #print possibleStrings 169 | for s in possibleStrings: 170 | if(currFreq in allstrings): 171 | allstrings[currFreq].append(s) 172 | else: 173 | allstrings[currFreq] = [s] 174 | if((len(allstrings[currFreq]) > results.lockNum) and lockOnSignal == True): 175 | lockedFreq = True 176 | 177 | n2=dt.datetime.now() 178 | if(((n2-n1).microseconds * 1000) >= results.timeStepFreq): 179 | if(lockedFreq == False): 180 | currFreq += results.stepFreq 181 | if(currFreq > results.endFreq): 182 | currFreq = results.startFreq 183 | n1=dt.datetime.now() 184 | d.setFreq(currFreq) 185 | 186 | except KeyboardInterrupt: 187 | break 188 | except ChipconUsbTimeoutException: 189 | pass 190 | 191 | termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings) 192 | # hacky, but i wanna get rid of the line: 193 | print "\r" + (" " * 150) 194 | 195 | 196 | print "Scanning stopped, keys found in following frequencies:" 197 | sortedKeys = sorted(allstrings, key=lambda k: len(allstrings[k]), reverse=True) 198 | 199 | num = 1; 200 | if(results.verbose == True): 201 | print "**VERBOSE** ALL keys found:" 202 | for sK in sortedKeys: 203 | currLen = len(allstrings[sK]) 204 | print num,": ",str(sK)," - Num signals Found:",currLen 205 | print "-----------------------------------------------" 206 | for verbose_key in allstrings[sK]: 207 | print verbose_key 208 | num=num+1 209 | num = 1; 210 | for sK in sortedKeys: 211 | currLen = len(allstrings[sK]) 212 | print num,": ",str(sK)," - Num signals Found:",currLen 213 | num=num+1 214 | 215 | 216 | if(results.replayKey == True and len(sortedKeys) > 0): 217 | var = 200; 218 | while((var < 0) or (var > len(sortedKeys))): 219 | try: 220 | var = int(raw_input("Please enter key to use for replaying: ")) 221 | except ValueError: 222 | pass 223 | allstrings = allstrings[sortedKeys[var-1]] 224 | d.setFreq(sortedKeys[var-1]) 225 | 226 | for a in allstrings: 227 | currLen = len(a) 228 | if currLen in lens.keys(): 229 | lens[currLen] = lens[currLen] + 1 230 | else: 231 | lens[currLen] = 1 232 | 233 | sorted_lens = sorted(lens.items(), key=operator.itemgetter(1), reverse=True) 234 | if len(sorted_lens) > 0: 235 | searchLen = sorted_lens[0][0] 236 | if(results.verbose == True): 237 | print "\nFound most keys in string have a length of " + str(searchLen) + " using those keys to calculate common key." 238 | foundKeys = [] 239 | for a in allstrings: 240 | if(len(a) == searchLen): 241 | foundKeys.append(bin(int(a,16))[2:]) 242 | #print bin(int(a,16)) 243 | 244 | 245 | maxlen = 0; 246 | for foundKey in foundKeys: 247 | if len(foundKey) > maxlen: 248 | maxlen = len(foundKey) 249 | #print maxlen 250 | for i in range(0,len(foundKeys)): 251 | if(len(foundKeys[i]) < maxlen): 252 | foundKeys[i] = foundKeys[i] + ("0" * (maxlen - len(foundKeys[i]))) 253 | 254 | finalKey = ""; 255 | for charPos in range(0,maxlen): 256 | total = 0; 257 | for i in range(0,len(foundKeys)): 258 | thisChar = foundKeys[i][charPos] 259 | total += int(thisChar) 260 | if(total > (len(foundKeys) / 2)): 261 | finalKey += "1" 262 | else: 263 | finalKey += "0" 264 | if(results.verbose == True): 265 | print "\nUsing Final Key as:" 266 | print BOLD + "BIN:" + ENDC + str(finalKey) 267 | 268 | key_packed = bitstring.BitArray(bin=finalKey).tobytes() 269 | 270 | keyLen = len(key_packed) 271 | 272 | print "[+] Key len:\n\t",keyLen,"" 273 | print "[+] Key:\n\t", key_packed.encode('hex') 274 | print "[+] Freq:\n\t", str(sortedKeys[var-1]) 275 | 276 | d.makePktFLEN(keyLen) 277 | 278 | print "[+] Transmitting key: ",repeatNum," times" 279 | barSize = 50 280 | for i in range(0,repeatNum): 281 | d.RFxmit(key_packed) 282 | progress = (float(i+1)/repeatNum) 283 | progVal = int(round(progress * barSize)) 284 | progressBar = ("#" * progVal) + (" " * (barSize - progVal)) 285 | sys.stdout.write("\r\tPercent: [ " + str(progressBar) + " ] " + str(int(progress * 100)) + "%") 286 | sys.stdout.flush() 287 | 288 | sys.stdout.write("\nDone.\n") 289 | d.setModeIDLE() 290 | 291 | 292 | else: 293 | print "\n\nNo keys found :(\nbye." 294 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RfCatHelpers 2 | Helper scripts for RfCat devices 3 | 4 | # AM OOK Scanner 5 | Script listens for OOK signals (starting with multiple 0's), converts this to binary and compares to other signals it has seen, it will then try and calculate the accurate final signal (based on normalising all the signals). 6 | 7 | # AM OOK Transmit 8 | Simple script to take the binary you wish to send and ... well ... send it. Will either convert your binary (by creating the 0's and 1's for AM/OOK) or send out a full binary (if conversion is already done, say from listening with the Scanner) 9 | 10 | # PWMScanner 11 | This looks for AM/OOK signals and 'locks' onto them to show you the outputted script as well as offers you the ability to replay a captured signal, useful for basic testing, but ultimately not something you want to use when doing more advanced testing 12 | 13 | # RF Jammer 14 | Script to Jam on a single frequency, useful for stopping transmissions from going through to replay them 15 | 16 | # RF Simple replay 17 | Listens for a 'packet' ( re.search(r'((0)\2{15,})', x ) and stores a particular amount of these packets (configured) then replays them when the user presses a key 18 | 19 | # RF Simple transmit 20 | Resends packets captured from the about (use -o for out/in file for both) 21 | 22 | # Dual RF 23 | Ability to use 2 RFCat compatible devices to search and jam -- not recommended for anything but laughing at my code 24 | -------------------------------------------------------------------------------- /RFJammer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import datetime as dt 5 | from rflib import * 6 | from struct import * 7 | import bitstring 8 | import operator 9 | import time 10 | import argparse 11 | 12 | keyLen = 0 13 | baudRate = 4800 14 | frequency = 433880000 15 | repeatNum = 25 16 | 17 | def ConfigureD(d): 18 | d.setMdmModulation(MOD_ASK_OOK) 19 | d.setFreq(frequency) 20 | d.makePktFLEN(0) 21 | d.setMdmDRate(4800) 22 | 23 | def jamFreq(d,freq): 24 | d.setModeTX() 25 | 26 | def stopJam(d): 27 | d.setModeIDLE() 28 | 29 | 30 | 31 | parser = argparse.ArgumentParser(description='Application to use a RFCat compatible device to Jam a particular frequency',version="0.1-bricktop") 32 | parser.add_argument('-f', action="store", default="433880000", dest="baseFreq",help='Target frequency to listen for remote (default 433880000)',type=int) 33 | parser.add_argument('-r', action="store", dest="baudRate",default=4800,help='Baudrate, defaults to 4800',type=int) 34 | parser.add_argument('-t', action="store", dest="jamTime",default=15,help='Seconds to jam for, defaults to 15 seconds',type=int) 35 | parser.add_argument('-p', action="store", default="100", dest="power",help='Power level for re-transmitting',type=int) 36 | results = parser.parse_args() 37 | 38 | print "Configuring RfCat" 39 | d = RfCat() 40 | d.setMdmModulation(MOD_ASK_OOK) 41 | d.setFreq(results.baseFreq) 42 | d.setMdmSyncMode(0) 43 | d.setMdmDRate(results.baudRate) 44 | d.setMdmChanSpc(24000) 45 | d.setModeIDLE() 46 | d.setPower(results.power) 47 | 48 | 49 | 50 | abort_after = results.jamTime 51 | start = time.time() 52 | print "Starting Jam on " + str(results.baseFreq) 53 | try: 54 | jamFreq(d,results.baseFreq) 55 | except ChipconUsbTimeoutException: 56 | pass 57 | 58 | while True: 59 | delta = time.time() - start 60 | if delta >= abort_after: 61 | break 62 | 63 | print "Completed Jam" 64 | d.setModeIDLE() 65 | -------------------------------------------------------------------------------- /RFSimpleReplay.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | from rflib import * 5 | from struct import * 6 | import bitstring 7 | import operator 8 | import argparse 9 | import time 10 | import pickle 11 | 12 | parser = argparse.ArgumentParser(description='Dumb application to replay a signal',version="0.1-bricktop") 13 | parser.add_argument('-f', action="store", default="433880000", dest="baseFreq",help='Target frequency to listen for remote (default 433880000)',type=int) 14 | parser.add_argument('-r', action="store", dest="baudRate",default=4800,help='Baudrate, defaults to 4800',type=int) 15 | parser.add_argument('-n', action="store", dest="numSignals",default=3,help='Number of signals to capture before replaying',type=int) 16 | parser.add_argument('-i', action="store", default="24000", dest="chanWidth",help='Width of each channel (lowest being 24000 -- default)',type=int) 17 | parser.add_argument('-o', action="store", default="", dest="outFile",help='output file to save to') 18 | parser.add_argument('-p', action="store", default="100", dest="power",help='Power level for re-transmitting',type=int) 19 | parser.add_argument('-m', action="store", default="-40", dest="minRSSI",help='Minimum RSSI db to accept signal',type=int) 20 | parser.add_argument('-c', action="store", default="60000", dest="chanBW",help='Channel BW for RX',type=int) 21 | parser.add_argument('-k', action="store", dest="waitForKeypress", default=True,help='Wait for keypress before resending') 22 | results = parser.parse_args() 23 | 24 | rawCapture = []; 25 | print "Configuring RfCat" 26 | d = RfCat() 27 | d.setMdmModulation(MOD_ASK_OOK) 28 | d.setFreq(results.baseFreq) 29 | d.setMdmSyncMode(0) 30 | d.setMdmDRate(results.baudRate) 31 | d.setMdmChanBW(results.chanBW) 32 | d.setMdmChanSpc(results.chanWidth) 33 | d.setChannel(0) 34 | d.setPower(results.power) 35 | d.lowball(1) 36 | 37 | print "Searching..." 38 | while True: 39 | try: 40 | 41 | y, t = d.RFrecv(1) 42 | sampleString=y.encode('hex') 43 | #print sampleString 44 | strength= 0 - ord(str(d.getRSSI())) 45 | 46 | #sampleString = re.sub(r'((f)\2{8,})', '',sampleString) 47 | if (re.search(r'((0)\2{15,})', sampleString)): 48 | print "Signal Strength:" + str(strength) 49 | if(strength > results.minRSSI): 50 | rawCapture.append(sampleString) 51 | print "Found " + str(sampleString) 52 | if(len(rawCapture) >= results.numSignals): 53 | break; 54 | 55 | 56 | 57 | 58 | except ChipconUsbTimeoutException: 59 | pass 60 | except KeyboardInterrupt: 61 | break 62 | print "Saving phase" 63 | outputCapture = rawCapture 64 | if(results.outFile != ''): 65 | pickle.dump(outputCapture, open(results.outFile,"wb")) 66 | print "Send Phase..." 67 | #print rawCapture 68 | emptykey = '\x00\x00\x00\x00\x00\x00\x00' 69 | d.makePktFLEN(len(emptykey)) 70 | d.RFxmit(emptykey) 71 | while True: 72 | try: 73 | freq = raw_input("Press to resend or type the frequency you wish to send on now:") 74 | if(freq != ''): 75 | d.setFreq(int(freq)) 76 | 77 | for i in range(0,len(rawCapture)): 78 | key_packed = bitstring.BitArray(hex=rawCapture[i]).tobytes() 79 | if(results.waitForKeypress == True): 80 | raw_input(" Press any key to send " + str(i+1) + " of " + str(len(rawCapture))) 81 | d.makePktFLEN(len(key_packed)) 82 | d.RFxmit(key_packed) 83 | print "Sent " + str(i+1) + " of " + str(len(rawCapture)) 84 | except KeyboardInterrupt: 85 | print "Bye!" 86 | d.setModeIDLE() 87 | sys.exit() 88 | break; 89 | print "exiting." 90 | d.setModeIDLE() 91 | -------------------------------------------------------------------------------- /RFSimpleTransmit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | from rflib import * 5 | from struct import * 6 | import bitstring 7 | import operator 8 | import argparse 9 | import time 10 | import pickle 11 | 12 | parser = argparse.ArgumentParser(description='Dumb application to replay a signal',version="0.1-bricktop") 13 | parser.add_argument('-f', action="store", default="433880000", dest="baseFreq",help='Target frequency to listen for remote (default 433880000)',type=int) 14 | parser.add_argument('-r', action="store", dest="baudRate",default=4800,help='Baudrate, defaults to 4800',type=int) 15 | parser.add_argument('-i', action="store", default="24000", dest="chanWidth",help='Width of each channel (lowest being 24000 -- default)',type=int) 16 | parser.add_argument('-p', action="store", default="100", dest="power",help='Power level for re-transmitting',type=int) 17 | parser.add_argument('-o', action="store", default="", required=True, dest="inFile",help='File to read in') 18 | parser.add_argument('-k', action="store", dest="waitForKeypress", default=True,help='Wait for keypress before resending') 19 | results = parser.parse_args() 20 | 21 | rawCapture = []; 22 | print "Configuring RfCat" 23 | d = RfCat() 24 | d.setMdmModulation(MOD_ASK_OOK) 25 | d.setFreq(results.baseFreq) 26 | d.setMdmSyncMode(0) 27 | d.setMdmDRate(results.baudRate) 28 | d.setMdmChanSpc(results.chanWidth) 29 | d.setChannel(0) 30 | d.setPower(results.power) 31 | d.setMaxPower() 32 | #d.setRFRegister(PA_TABLE0, 0xFF) 33 | #d.setRFRegister(PA_TABLE1, 0xFF) 34 | 35 | 36 | rawCapture = pickle.load(open(results.inFile,"rb")) 37 | if(len(rawCapture) == 0): 38 | print "No captures found" 39 | sys.exit() 40 | else: 41 | print "loaded" + str(len(rawCapture)) 42 | print "Send Phase..." 43 | 44 | emptykey = '\x00\x00\x00\x00\x00\x00\x00' 45 | d.makePktFLEN(len(emptykey)) 46 | d.RFxmit(emptykey) 47 | while True: 48 | try: 49 | freq = raw_input("Press to resend or type the frequency you wish to send on now:") 50 | if(freq != ''): 51 | d.setFreq(int(freq)) 52 | 53 | for i in range(0,len(rawCapture)): 54 | key_packed = bitstring.BitArray(hex=rawCapture[i]).tobytes() 55 | d.makePktFLEN(len(key_packed)) 56 | if(results.waitForKeypress == True): 57 | raw_input(" Press any key to send " + str(i+1) + " of " + str(len(rawCapture))) 58 | d.RFxmit(key_packed) 59 | print "Sent " + str(i+1) + " of " + str(len(rawCapture)) 60 | except KeyboardInterrupt: 61 | print "Bye!" 62 | d.setModeIDLE() 63 | sys.exit() 64 | break; 65 | print "exiting." 66 | d.setModeIDLE() 67 | #d.setRFRegister(PA_TABLE0, 0x00) 68 | #d.setRFRegister(PA_TABLE1, 0x00) 69 | -------------------------------------------------------------------------------- /decodeOOK.py: -------------------------------------------------------------------------------- 1 | import wave 2 | import sys 3 | from struct import * 4 | from collections import Counter 5 | 6 | track=wave.open(sys.argv[1]) 7 | frames=track.getnframes() 8 | n= 0 9 | max= 0 10 | samples= [] 11 | keylen = 12 12 | avg = 0; 13 | 14 | print "number of frames: {}".format(frames) 15 | 16 | while n < frames: 17 | n += 1 18 | current = 0 19 | frame = track.readframes(1) 20 | 21 | if len(frame) != 2 and len(frame) != 4: 22 | continue 23 | 24 | left_channel = frame[:2] 25 | 26 | current= unpack(" max: 28 | max= current; 29 | samples.append(current) 30 | 31 | 32 | avg = sum(samples) / len(samples) 33 | avg = avg + (max/100) + 100 34 | 35 | print "Max: ",str(max),"Average: ",str(avg) 36 | 37 | 38 | peaks= [] 39 | foundKeys = [] 40 | sPeak = 0 #startPeak 41 | ePeak = 0 #endPeak 42 | minPeakDistance = 1 #minimum peak distance 43 | 44 | for currFrame in range(0,len(samples)): 45 | if(samples[currFrame] > avg): 46 | if(sPeak == 0): 47 | sPeak = currFrame #~starts here 48 | else: 49 | if(sPeak != 0 and (sPeak+minPeakDistance) < currFrame ): 50 | ePeak = currFrame 51 | distance = ePeak - sPeak 52 | peaks.append({"d":distance,"s":ePeak,"e":ePeak}) 53 | sPeak = 0 54 | currentSeg = [] 55 | n = 0 56 | 57 | #get average peak distance (to see dividers between signals) 58 | #avgPeakLen = sum(peaks["d"]) / len(peaks) 59 | #avgPeakLen = sum([p['d'] for p in peaks]) / len(peaks) 60 | avgPeakLen = sum([peaks[i+1]['s'] - p['e'] for i, p in enumerate(peaks) if i < len(peaks) - 1])/(i + 1) 61 | avgPeakLen = avgPeakLen * 3 62 | print "avgpl:",avgPeakLen 63 | print "len Peaks",len(peaks) 64 | 65 | tmpBin = "" 66 | while n < len(peaks): 67 | #if(peaks[n]["d"] > avgPeakLen and len(currentSeg) > 0): 68 | if(n > 1): 69 | diff = peaks[n]["s"] - peaks[n-1]["e"] 70 | #if (diff > 30): 71 | #print diff 72 | 73 | if(n > 1 and ((peaks[n]["s"] - peaks[n-1]["e"]) > avgPeakLen and len(currentSeg) > 1)): 74 | mean = (sum(currentSeg) / len(currentSeg)) - 1 75 | for c in currentSeg: 76 | if ( c > mean ): 77 | tmpBin += "0" 78 | else: 79 | tmpBin += "1" 80 | foundKeys.append(tmpBin) 81 | #print tmpBin 82 | tmpBin = ""; 83 | currentSeg = [] 84 | else: 85 | currentSeg.append(peaks[n]["d"]) 86 | n= n + 1 87 | 88 | #leftovers 89 | if (len(currentSeg) > 0): 90 | mean = (sum(currentSeg) / len(currentSeg)) - 1 91 | for c in currentSeg: 92 | if ( c > mean ): 93 | tmpBin += "0" 94 | else: 95 | tmpBin += "1" 96 | foundKeys.append(tmpBin) 97 | tmpBin = ""; 98 | 99 | keyList = Counter(foundKeys) 100 | print "\n\n(Top)Found Keys:" 101 | 102 | 103 | 104 | x = 0 105 | for k, v in keyList.most_common(10): 106 | if (len(k) > 2 and int(k,2) != 0): 107 | x+=1 108 | print x,":",k,"(",v,") - len:",len(k), "Hex:",hex(int(k,2)) 109 | --------------------------------------------------------------------------------