1 //-----------------------------------------------------------------------------
3 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
4 // at your option, any later version. See the LICENSE.txt file for the text of
6 //-----------------------------------------------------------------------------
7 // Low frequency Presco tag commands
8 //-----------------------------------------------------------------------------
10 #include "cmdlfpresco.h"
15 #include "commonutil.h" // ARRAYLEN
16 #include "cmdparser.h" // command_t
21 #include "protocols.h" // for T55xx config register definitions
22 #include "lfdemod.h" // parityTest
23 #include "cmdlft55xx.h" // verifywrite
24 #include "cmdlfem4x05.h" //
25 #include "cliparser.h"
27 static int CmdHelp(const char *Cmd
);
29 // find presco preamble 0x10D in already demoded data
30 static int detectPresco(uint8_t *dest
, size_t *size
) {
31 if (*size
< 128 * 2) return -1; //make sure buffer has data
33 uint8_t preamble
[] = {0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
34 if (!preambleSearch(dest
, preamble
, sizeof(preamble
), size
, &startIdx
))
35 return -2; //preamble not found
36 if (*size
!= 128) return -3; //wrong demoded size
37 //return start position
41 // convert base 12 ID to sitecode & usercode & 8 bit other unknown code
42 static int getWiegandFromPrintedPresco(void *arr
, uint32_t *fullcode
) {
43 char *s
= (char *)arr
;
45 for (int i
= 0; i
< strlen(s
); ++i
) {
46 // Get value from number string.
51 if (s
[i
] >= 0x30 && s
[i
] <= 0x39)
56 // last digit is only added, not multipled.
57 if (i
< strlen(s
) - 1)
63 // calc not certain - intended to get bitstream for programming / sim
64 static int getPrescoBits(uint32_t fullcode
, uint8_t *prescoBits
) {
65 num_to_bytebits(0x10D00000, 32, prescoBits
);
66 num_to_bytebits(0x00000000, 32, prescoBits
+ 32);
67 num_to_bytebits(0x00000000, 32, prescoBits
+ 64);
68 num_to_bytebits(fullcode
, 32, prescoBits
+ 96);
72 //see ASKDemod for what args are accepted
73 int demodPresco(bool verbose
) {
74 (void) verbose
; // unused so far
76 if (ASKDemod_ext(32, 0, 0, 0, false, false, false, 1, &st
) != PM3_SUCCESS
) {
77 PrintAndLogEx(DEBUG
, "DEBUG: Error Presco ASKDemod failed");
80 size_t size
= DemodBufferLen
;
81 int ans
= detectPresco(DemodBuffer
, &size
);
84 PrintAndLogEx(DEBUG
, "DEBUG: Error - Presco: too few bits found");
86 PrintAndLogEx(DEBUG
, "DEBUG: Error - Presco: preamble not found");
88 PrintAndLogEx(DEBUG
, "DEBUG: Error - Presco: Size not correct: %zu", size
);
90 PrintAndLogEx(DEBUG
, "DEBUG: Error - Presco: ans: %d", ans
);
93 setDemodBuff(DemodBuffer
, 128, ans
);
94 setClockGrid(g_DemodClock
, g_DemodStartIdx
+ (ans
* g_DemodClock
));
97 uint32_t raw1
= bytebits_to_byte(DemodBuffer
, 32);
98 uint32_t raw2
= bytebits_to_byte(DemodBuffer
+ 32, 32);
99 uint32_t raw3
= bytebits_to_byte(DemodBuffer
+ 64, 32);
100 uint32_t raw4
= bytebits_to_byte(DemodBuffer
+ 96, 32);
101 uint32_t fullcode
= raw4
;
102 uint32_t usercode
= fullcode
& 0x0000FFFF;
103 uint32_t sitecode
= (fullcode
>> 24) & 0x000000FF;
105 PrintAndLogEx(SUCCESS
, "Presco Site code: " _GREEN_("%u") " User code: " _GREEN_("%u") " Full code: " _GREEN_("%08X") " Raw: " _YELLOW_("%08X%08X%08X%08X")
109 , raw1
, raw2
, raw3
, raw4
114 static int CmdPrescoDemod(const char *Cmd
) {
115 CLIParserContext
*ctx
;
116 CLIParserInit(&ctx
, "lf presco demod",
117 "Try to find presco preamble, if found decode / descramble data",
125 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
127 return demodPresco(true);
130 //see ASKDemod for what args are accepted
131 static int CmdPrescoReader(const char *Cmd
) {
132 CLIParserContext
*ctx
;
133 CLIParserInit(&ctx
, "lf presco reader",
135 "lf presco reader -@ -> continuous reader mode"
140 arg_lit0("@", NULL
, "optional - continuous reader mode"),
143 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
144 bool cm
= arg_get_lit(ctx
, 1);
148 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
152 lf_read(false, 12000);
154 } while (cm
&& !kbd_enter_pressed());
158 // takes base 12 ID converts to hex
159 // Or takes 8 digit hex ID
160 static int CmdPrescoClone(const char *Cmd
) {
161 CLIParserContext
*ctx
;
162 CLIParserInit(&ctx
, "lf presco clone",
163 "clone a presco tag to a T55x7, Q5/T5555 or EM4305/4469 tag.",
164 "lf presco clone -d 018363467\n"
165 "lf presco clone -d 018363467 --q5 -> encode for Q5/T5555 tag\n"
166 "lf presco clone -d 018363467 --em -> encode for EM4305/4469"
171 arg_str0("c", NULL
, "<hex>", "8 digit hex card number"),
172 arg_str0("d", NULL
, "<digits>", "9 digit presco card ID"),
173 arg_lit0(NULL
, "q5", "optional - specify writing to Q5/T5555 tag"),
174 arg_lit0(NULL
, "em", "optional - specify writing to EM4305/4469 tag"),
177 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
180 uint8_t hex
[4] = {0, 0, 0, 0};
181 CLIGetHexWithReturn(ctx
, 1, hex
, &hex_len
);
185 memset(idstr
, 0x00, sizeof(idstr
));
186 CLIGetStrWithReturn(ctx
, 2, idstr
, &slen
);
188 bool q5
= arg_get_lit(ctx
, 3);
189 bool em
= arg_get_lit(ctx
, 4);
193 PrintAndLogEx(FAILED
, "Can't specify both Q5 and EM4305 at the same time");
197 uint32_t fullcode
= 0;
200 fullcode
= bytes_to_num(hex
, hex_len
);
202 //param get string int param_getstr(const char *line, int paramnum, char * str)
204 PrintAndLogEx(ERR
, "Must contain atleast 2 digits");
208 getWiegandFromPrintedPresco(idstr
, &fullcode
);
211 uint32_t usercode
= fullcode
& 0x0000FFFF; //% 65566
212 uint32_t sitecode
= (fullcode
>> 24) & 0x000000FF; // /= 16777216;
214 uint32_t blocks
[5] = {T55x7_MODULATION_MANCHESTER
| T55x7_BITRATE_RF_32
| 4 << T55x7_MAXBLOCK_SHIFT
| T55x7_ST_TERMINATOR
, 0, 0, 0, 0};
216 char cardtype
[16] = {"T55x7"};
219 blocks
[0] = T5555_FIXED
| T5555_MODULATION_MANCHESTER
| T5555_SET_BITRATE(32) | 4 << T5555_MAXBLOCK_SHIFT
| T5555_ST_TERMINATOR
;
220 snprintf(cardtype
, sizeof(cardtype
), "Q5/T5555");
225 blocks
[0] = EM4305_PRESCO_CONFIG_BLOCK
;
226 snprintf(cardtype
, sizeof(cardtype
), "EM4305/4469");
229 if ((sitecode
& 0xFF) != sitecode
) {
231 PrintAndLogEx(INFO
, "Site code truncated to 8-bits (Presco): %u", sitecode
);
234 if ((usercode
& 0xFFFF) != usercode
) {
236 PrintAndLogEx(INFO
, "User code truncated to 16-bits (Presco): %u", usercode
);
239 blocks
[1] = 0x10D00000; //preamble
240 blocks
[2] = 0x00000000;
241 blocks
[3] = 0x00000000;
242 blocks
[4] = fullcode
;
244 PrintAndLogEx(INFO
, "Preparing to clone Presco to " _GREEN_("%s") " with Site code: " _GREEN_("%u") " User code: " _GREEN_("%u") " Full code: " _GREEN_("%08x")
250 print_blocks(blocks
, ARRAYLEN(blocks
));
254 res
= em4x05_clone_tag(blocks
, ARRAYLEN(blocks
), 0, false);
256 res
= clone_t55xx_tag(blocks
, ARRAYLEN(blocks
));
258 PrintAndLogEx(SUCCESS
, "Done");
259 PrintAndLogEx(HINT
, "Hint: try " _YELLOW_("`lf presco reader`") " to verify");
263 // takes base 12 ID converts to hex
264 // Or takes 8 digit hex ID
265 static int CmdPrescoSim(const char *Cmd
) {
266 CLIParserContext
*ctx
;
267 CLIParserInit(&ctx
, "lf presco sim",
268 "Enables simulation of presco card with specified card number.\n"
269 "Simulation runs until the button is pressed or another USB command is issued.\n"
270 "Per presco format, the card number is 9 digit number and can contain *# chars. Larger values are truncated.",
271 "lf presco sim -d 018363467"
276 arg_str0("c", NULL
, "<hex>", "8 digit hex card number"),
277 arg_str0("d", NULL
, "<digits>", "9 digit presco card ID"),
280 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
283 uint8_t hex
[4] = {0, 0, 0, 0};
284 CLIGetHexWithReturn(ctx
, 1, hex
, &hex_len
);
288 memset(idstr
, 0x00, sizeof(idstr
));
289 CLIGetStrWithReturn(ctx
, 2, idstr
, &slen
);
292 uint32_t fullcode
= 0;
295 fullcode
= bytes_to_num(hex
, hex_len
);
298 PrintAndLogEx(ERR
, "Must contain atleast 2 digits");
301 getWiegandFromPrintedPresco(idstr
, &fullcode
);
304 uint32_t usercode
= fullcode
& 0x0000FFFF;
305 uint32_t sitecode
= (fullcode
>> 24) & 0x000000FF;
307 if ((sitecode
& 0xFF) != sitecode
) {
309 PrintAndLogEx(INFO
, "Site code truncated to 8-bits (Presco): %u", sitecode
);
312 if ((usercode
& 0xFFFF) != usercode
) {
314 PrintAndLogEx(INFO
, "User code truncated to 16-bits (Presco): %u", usercode
);
317 PrintAndLogEx(SUCCESS
, "Simulating Presco - Site Code: " _GREEN_("%u") " User Code: " _GREEN_("%u") " Full Code: " _GREEN_("%08X")
324 getPrescoBits(fullcode
, bs
);
326 lf_asksim_t
*payload
= calloc(1, sizeof(lf_asksim_t
) + sizeof(bs
));
327 payload
->encoding
= 1;
329 payload
->separator
= 1;
331 memcpy(payload
->data
, bs
, sizeof(bs
));
333 clearCommandBuffer();
334 SendCommandNG(CMD_LF_ASK_SIMULATE
, (uint8_t *)payload
, sizeof(lf_asksim_t
) + sizeof(bs
));
337 PacketResponseNG resp
;
338 WaitForResponse(CMD_LF_ASK_SIMULATE
, &resp
);
340 PrintAndLogEx(INFO
, "Done");
341 if (resp
.status
!= PM3_EOPABORTED
)
346 static command_t CommandTable
[] = {
347 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
348 {"demod", CmdPrescoDemod
, AlwaysAvailable
, "demodulate Presco tag from the GraphBuffer"},
349 {"reader", CmdPrescoReader
, IfPm3Lf
, "attempt to read and extract tag data"},
350 {"clone", CmdPrescoClone
, IfPm3Lf
, "clone presco tag to T55x7 or Q5/T5555"},
351 {"sim", CmdPrescoSim
, IfPm3Lf
, "simulate presco tag"},
352 {NULL
, NULL
, NULL
, NULL
}
355 static int CmdHelp(const char *Cmd
) {
356 (void)Cmd
; // Cmd is not used so far
357 CmdsHelp(CommandTable
);
361 int CmdLFPresco(const char *Cmd
) {
362 clearCommandBuffer();
363 return CmdsParse(CommandTable
, Cmd
);