1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // Low frequency Presco tag commands
17 //-----------------------------------------------------------------------------
19 #include "cmdlfpresco.h"
24 #include "commonutil.h" // ARRAYLEN
25 #include "cmdparser.h" // command_t
30 #include "protocols.h" // for T55xx config register definitions
31 #include "lfdemod.h" // parityTest
32 #include "cmdlft55xx.h" // verifywrite
33 #include "cmdlfem4x05.h" //
34 #include "cliparser.h"
36 static int CmdHelp(const char *Cmd
);
38 // find presco preamble 0x10D in already demoded data
39 static int detectPresco(uint8_t *dest
, size_t *size
) {
40 if (*size
< 128 * 2) return -1; //make sure buffer has data
42 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};
43 if (!preambleSearch(dest
, preamble
, sizeof(preamble
), size
, &startIdx
))
44 return -2; //preamble not found
45 if (*size
!= 128) return -3; //wrong demoded size
46 //return start position
50 // convert base 12 ID to sitecode & usercode & 8 bit other unknown code
51 static int getWiegandFromPrintedPresco(void *arr
, uint32_t *fullcode
) {
52 char *s
= (char *)arr
;
54 for (int i
= 0; i
< strlen(s
); ++i
) {
55 // Get value from number string.
60 if (s
[i
] >= 0x30 && s
[i
] <= 0x39)
65 // last digit is only added, not multiplied.
66 if (i
< strlen(s
) - 1)
72 // calc not certain - intended to get bitstream for programming / sim
73 static int getPrescoBits(uint32_t fullcode
, uint8_t *prescoBits
) {
74 num_to_bytebits(0x10D00000, 32, prescoBits
);
75 num_to_bytebits(0x00000000, 32, prescoBits
+ 32);
76 num_to_bytebits(0x00000000, 32, prescoBits
+ 64);
77 num_to_bytebits(fullcode
, 32, prescoBits
+ 96);
81 //see ASKDemod for what args are accepted
82 int demodPresco(bool verbose
) {
83 (void) verbose
; // unused so far
85 if (ASKDemod_ext(32, 0, 0, 0, false, false, false, 1, &st
) != PM3_SUCCESS
) {
86 PrintAndLogEx(DEBUG
, "DEBUG: Error Presco ASKDemod failed");
89 size_t size
= g_DemodBufferLen
;
90 int ans
= detectPresco(g_DemodBuffer
, &size
);
93 PrintAndLogEx(DEBUG
, "DEBUG: Error - Presco: too few bits found");
95 PrintAndLogEx(DEBUG
, "DEBUG: Error - Presco: preamble not found");
97 PrintAndLogEx(DEBUG
, "DEBUG: Error - Presco: Size not correct: %zu", size
);
99 PrintAndLogEx(DEBUG
, "DEBUG: Error - Presco: ans: %d", ans
);
102 setDemodBuff(g_DemodBuffer
, 128, ans
);
103 setClockGrid(g_DemodClock
, g_DemodStartIdx
+ (ans
* g_DemodClock
));
106 uint32_t raw1
= bytebits_to_byte(g_DemodBuffer
, 32);
107 uint32_t raw2
= bytebits_to_byte(g_DemodBuffer
+ 32, 32);
108 uint32_t raw3
= bytebits_to_byte(g_DemodBuffer
+ 64, 32);
109 uint32_t raw4
= bytebits_to_byte(g_DemodBuffer
+ 96, 32);
110 uint32_t fullcode
= raw4
;
111 uint32_t usercode
= fullcode
& 0x0000FFFF;
112 uint32_t sitecode
= (fullcode
>> 24) & 0x000000FF;
114 PrintAndLogEx(SUCCESS
, "Presco Site code: " _GREEN_("%u") " User code: " _GREEN_("%u") " Full code: " _GREEN_("%08X") " Raw: " _YELLOW_("%08X%08X%08X%08X")
118 , raw1
, raw2
, raw3
, raw4
123 static int CmdPrescoDemod(const char *Cmd
) {
124 CLIParserContext
*ctx
;
125 CLIParserInit(&ctx
, "lf presco demod",
126 "Try to find presco preamble, if found decode / descramble data",
134 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
136 return demodPresco(true);
139 //see ASKDemod for what args are accepted
140 static int CmdPrescoReader(const char *Cmd
) {
141 CLIParserContext
*ctx
;
142 CLIParserInit(&ctx
, "lf presco reader",
144 "lf presco reader -@ -> continuous reader mode"
149 arg_lit0("@", NULL
, "optional - continuous reader mode"),
152 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
153 bool cm
= arg_get_lit(ctx
, 1);
157 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
161 lf_read(false, 12000);
163 } while (cm
&& !kbd_enter_pressed());
167 // takes base 12 ID converts to hex
168 // Or takes 8 digit hex ID
169 static int CmdPrescoClone(const char *Cmd
) {
170 CLIParserContext
*ctx
;
171 CLIParserInit(&ctx
, "lf presco clone",
172 "clone a presco tag to a T55x7, Q5/T5555 or EM4305/4469 tag.",
173 "lf presco clone -d 018363467 -> encode for T55x7 tag\n"
174 "lf presco clone -d 018363467 --q5 -> encode for Q5/T5555 tag\n"
175 "lf presco clone -d 018363467 --em -> encode for EM4305/4469"
180 arg_str0("c", NULL
, "<hex>", "8 digit hex card number"),
181 arg_str0("d", NULL
, "<digits>", "9 digit presco card ID"),
182 arg_lit0(NULL
, "q5", "optional - specify writing to Q5/T5555 tag"),
183 arg_lit0(NULL
, "em", "optional - specify writing to EM4305/4469 tag"),
186 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
189 uint8_t hex
[4] = {0, 0, 0, 0};
190 CLIGetHexWithReturn(ctx
, 1, hex
, &hex_len
);
193 int slen
= sizeof(idstr
) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
194 memset(idstr
, 0x00, sizeof(idstr
));
195 CLIGetStrWithReturn(ctx
, 2, idstr
, &slen
);
197 bool q5
= arg_get_lit(ctx
, 3);
198 bool em
= arg_get_lit(ctx
, 4);
202 PrintAndLogEx(FAILED
, "Can't specify both Q5 and EM4305 at the same time");
206 uint32_t fullcode
= 0;
209 fullcode
= bytes_to_num(hex
, hex_len
);
211 //param get string int param_getstr(const char *line, int paramnum, char * str)
213 PrintAndLogEx(ERR
, "Must contain atleast 2 digits");
217 getWiegandFromPrintedPresco(idstr
, &fullcode
);
220 uint32_t usercode
= fullcode
& 0x0000FFFF; //% 65566
221 uint32_t sitecode
= (fullcode
>> 24) & 0x000000FF; // /= 16777216;
223 uint32_t blocks
[5] = {T55x7_MODULATION_MANCHESTER
| T55x7_BITRATE_RF_32
| 4 << T55x7_MAXBLOCK_SHIFT
| T55x7_ST_TERMINATOR
, 0, 0, 0, 0};
225 char cardtype
[16] = {"T55x7"};
228 blocks
[0] = T5555_FIXED
| T5555_MODULATION_MANCHESTER
| T5555_SET_BITRATE(32) | 4 << T5555_MAXBLOCK_SHIFT
| T5555_ST_TERMINATOR
;
229 snprintf(cardtype
, sizeof(cardtype
), "Q5/T5555");
234 blocks
[0] = EM4305_PRESCO_CONFIG_BLOCK
;
235 snprintf(cardtype
, sizeof(cardtype
), "EM4305/4469");
238 if ((sitecode
& 0xFF) != sitecode
) {
240 PrintAndLogEx(INFO
, "Site code truncated to 8-bits (Presco): %u", sitecode
);
243 if ((usercode
& 0xFFFF) != usercode
) {
245 PrintAndLogEx(INFO
, "User code truncated to 16-bits (Presco): %u", usercode
);
248 blocks
[1] = 0x10D00000; //preamble
249 blocks
[2] = 0x00000000;
250 blocks
[3] = 0x00000000;
251 blocks
[4] = fullcode
;
253 PrintAndLogEx(INFO
, "Preparing to clone Presco to " _GREEN_("%s") " with Site code: " _GREEN_("%u") " User code: " _GREEN_("%u") " Full code: " _GREEN_("%08x")
259 print_blocks(blocks
, ARRAYLEN(blocks
));
263 res
= em4x05_clone_tag(blocks
, ARRAYLEN(blocks
), 0, false);
265 res
= clone_t55xx_tag(blocks
, ARRAYLEN(blocks
));
267 PrintAndLogEx(SUCCESS
, "Done");
268 PrintAndLogEx(HINT
, "Hint: try " _YELLOW_("`lf presco reader`") " to verify");
272 // takes base 12 ID converts to hex
273 // Or takes 8 digit hex ID
274 static int CmdPrescoSim(const char *Cmd
) {
275 CLIParserContext
*ctx
;
276 CLIParserInit(&ctx
, "lf presco sim",
277 "Enables simulation of presco card with specified card number.\n"
278 "Simulation runs until the button is pressed or another USB command is issued.\n"
279 "Per presco format, the card number is 9 digit number and can contain *# chars. Larger values are truncated.",
280 "lf presco sim -d 018363467"
285 arg_str0("c", NULL
, "<hex>", "8 digit hex card number"),
286 arg_str0("d", NULL
, "<digits>", "9 digit presco card ID"),
289 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
292 uint8_t hex
[4] = {0, 0, 0, 0};
293 CLIGetHexWithReturn(ctx
, 1, hex
, &hex_len
);
295 uint8_t idstr
[10] = {0};
296 int slen
= sizeof(idstr
) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
297 memset(idstr
, 0x00, sizeof(idstr
));
298 CLIGetStrWithReturn(ctx
, 2, idstr
, &slen
);
301 uint32_t fullcode
= 0;
304 fullcode
= bytes_to_num(hex
, hex_len
);
307 PrintAndLogEx(ERR
, "Must contain atleast 2 digits");
310 getWiegandFromPrintedPresco(idstr
, &fullcode
);
313 uint32_t usercode
= fullcode
& 0x0000FFFF;
314 uint32_t sitecode
= (fullcode
>> 24) & 0x000000FF;
316 if ((sitecode
& 0xFF) != sitecode
) {
318 PrintAndLogEx(INFO
, "Site code truncated to 8-bits (Presco): %u", sitecode
);
321 if ((usercode
& 0xFFFF) != usercode
) {
323 PrintAndLogEx(INFO
, "User code truncated to 16-bits (Presco): %u", usercode
);
326 PrintAndLogEx(SUCCESS
, "Simulating Presco - Site Code: " _GREEN_("%u") " User Code: " _GREEN_("%u") " Full Code: " _GREEN_("%08X")
333 getPrescoBits(fullcode
, bs
);
335 lf_asksim_t
*payload
= calloc(1, sizeof(lf_asksim_t
) + sizeof(bs
));
336 payload
->encoding
= 1;
338 payload
->separator
= 1;
340 memcpy(payload
->data
, bs
, sizeof(bs
));
342 clearCommandBuffer();
343 SendCommandNG(CMD_LF_ASK_SIMULATE
, (uint8_t *)payload
, sizeof(lf_asksim_t
) + sizeof(bs
));
346 PacketResponseNG resp
;
347 WaitForResponse(CMD_LF_ASK_SIMULATE
, &resp
);
349 PrintAndLogEx(INFO
, "Done!");
350 if (resp
.status
!= PM3_EOPABORTED
) {
356 static command_t CommandTable
[] = {
357 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
358 {"demod", CmdPrescoDemod
, AlwaysAvailable
, "demodulate Presco tag from the GraphBuffer"},
359 {"reader", CmdPrescoReader
, IfPm3Lf
, "attempt to read and extract tag data"},
360 {"clone", CmdPrescoClone
, IfPm3Lf
, "clone presco tag to T55x7, Q5/T5555 or EM4305/4469"},
361 {"sim", CmdPrescoSim
, IfPm3Lf
, "simulate presco tag"},
362 {NULL
, NULL
, NULL
, NULL
}
365 static int CmdHelp(const char *Cmd
) {
366 (void)Cmd
; // Cmd is not used so far
367 CmdsHelp(CommandTable
);
371 int CmdLFPresco(const char *Cmd
) {
372 clearCommandBuffer();
373 return CmdsParse(CommandTable
, Cmd
);