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 GALLAGHER tag commands
17 // ASK/MAN, RF/32, 96 bits long (unknown cs) (0x00088060)
18 // sample Q5 , ASK RF/32, STT, 96 bits (3blocks) ( 0x9000F006)
19 //-----------------------------------------------------------------------------
21 #include "cmdlfgallagher.h"
22 #include "mifare/gallaghercore.h"
23 #include <string.h> // memcpy
24 #include <ctype.h> // tolower
26 #include "commonutil.h" // ARRAYLEN
28 #include "cmdparser.h" // command_t
33 #include "lfdemod.h" // preamble test
34 #include "protocols.h" // t55xx defines
35 #include "cmdlft55xx.h" // clone..
36 #include "crc.h" // CRC8/Cardx
37 #include "cmdlfem4x05.h" //
38 #include "cliparser.h"
40 static int CmdHelp(const char *Cmd
);
42 //see ASK/MAN Demod for what args are accepted
43 int demodGallagher(bool verbose
) {
44 (void) verbose
; // unused so far
46 if (ASKDemod_ext(32, 0, 100, 0, false, false, false, 1, &st
) != PM3_SUCCESS
) {
47 PrintAndLogEx(DEBUG
, "DEBUG: Error - GALLAGHER: ASKDemod failed");
51 size_t size
= g_DemodBufferLen
;
52 int ans
= detectGallagher(g_DemodBuffer
, &size
);
55 PrintAndLogEx(DEBUG
, "DEBUG: Error - GALLAGHER: too few bits found");
57 PrintAndLogEx(DEBUG
, "DEBUG: Error - GALLAGHER: preamble not found");
59 PrintAndLogEx(DEBUG
, "DEBUG: Error - GALLAGHER: Size not correct: %zu", size
);
61 PrintAndLogEx(DEBUG
, "DEBUG: Error - GALLAGHER: ans: %d", ans
);
65 setDemodBuff(g_DemodBuffer
, 96, ans
);
66 setClockGrid(g_DemodClock
, g_DemodStartIdx
+ (ans
* g_DemodClock
));
69 uint32_t raw1
= bytebits_to_byte(g_DemodBuffer
, 32);
70 uint32_t raw2
= bytebits_to_byte(g_DemodBuffer
+ 32, 32);
71 uint32_t raw3
= bytebits_to_byte(g_DemodBuffer
+ 64, 32);
75 for (int i
= 0, pos
= 0; i
< ARRAYLEN(arr
); i
++) {
76 // first 16 bits are the 7FEA prefix, then every 9th bit is a checksum-bit for the preceding byte
78 arr
[i
] = bytebits_to_byte(g_DemodBuffer
+ pos
, 8);
82 uint8_t crc
= bytebits_to_byte(g_DemodBuffer
+ 16 + (9 * 8), 8);
83 uint8_t calc_crc
= CRC8Cardx(arr
, ARRAYLEN(arr
));
85 GallagherCredentials_t creds
= {0};
86 gallagher_decode_creds(arr
, &creds
);
88 PrintAndLogEx(SUCCESS
, "GALLAGHER - Region: " _GREEN_("%u") " Facility: " _GREEN_("%u") " Card No.: " _GREEN_("%u") " Issue Level: " _GREEN_("%u"),
89 creds
.region_code
, creds
.facility_code
, creds
.card_number
, creds
.issue_level
);
90 PrintAndLogEx(SUCCESS
, " Displayed: " _GREEN_("%C%u"), creds
.region_code
+ 'A', creds
.facility_code
);
91 PrintAndLogEx(SUCCESS
, " Raw: %08X%08X%08X", raw1
, raw2
, raw3
);
92 PrintAndLogEx(SUCCESS
, " CRC: %02X - %02X (%s)", crc
, calc_crc
, (crc
== calc_crc
) ? "ok" : "fail");
96 static int CmdGallagherDemod(const char *Cmd
) {
97 CLIParserContext
*ctx
;
98 CLIParserInit(&ctx
, "lf gallagher demod",
99 "Try to find GALLAGHER preamble, if found decode / descramble data",
107 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
109 return demodGallagher(true);
112 static int CmdGallagherReader(const char *Cmd
) {
113 CLIParserContext
*ctx
;
114 CLIParserInit(&ctx
, "lf gallagher reader",
115 "read a GALLAGHER tag",
116 "lf gallagher reader -@ -> continuous reader mode"
121 arg_lit0("@", NULL
, "optional - continuous reader mode"),
124 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
125 bool cm
= arg_get_lit(ctx
, 1);
129 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
133 lf_read(false, 4096 * 2 + 20);
135 } while (cm
&& !kbd_enter_pressed());
139 static void setBitsInBlocks(uint32_t *blocks
, uint8_t *pos
, uint32_t data
, uint8_t data_len
) {
140 for (int i
= data_len
- 1; i
>= 0; i
--) {
141 uint8_t blk
= *pos
/ 32;
142 uint8_t bitPos
= 31 - *pos
% 32; // fill from left
143 uint8_t bit
= (data
>> i
) & 1;
144 blocks
[blk
] |= bit
<< bitPos
;
149 static void createBlocks(uint32_t *blocks
, GallagherCredentials_t
*creds
) {
150 // put data into the correct places (Gallagher obfuscation)
151 uint8_t arr
[8] = {0};
152 gallagher_encode_creds(arr
, creds
);
154 blocks
[0] = blocks
[1] = blocks
[2] = 0;
158 setBitsInBlocks(blocks
, &pos
, 0x7fea, 16);
160 for (int i
= 0; i
< ARRAYLEN(arr
); i
++) {
162 setBitsInBlocks(blocks
, &pos
, arr
[i
], 8);
164 // every byte is followed by a bit which is the inverse of the last bit
165 setBitsInBlocks(blocks
, &pos
, !(arr
[i
] & 0x1), 1);
169 uint8_t crc
= CRC8Cardx(arr
, ARRAYLEN(arr
));
170 setBitsInBlocks(blocks
, &pos
, crc
, 8);
173 static int CmdGallagherClone(const char *Cmd
) {
174 CLIParserContext
*ctx
;
175 CLIParserInit(&ctx
, "lf gallagher clone",
176 "clone a GALLAGHER tag to a T55x7, Q5/T5555 or EM4305/4469 tag.",
177 "lf gallagher clone --raw 0FFD5461A9DA1346B2D1AC32 -> encode for T55x7 tag\n"
178 "lf gallagher clone --raw 0FFD5461A9DA1346B2D1AC32 --q5 -> encode for Q5/T5555 tag\n"
179 "lf gallagher clone --raw 0FFD5461A9DA1346B2D1AC32 --em -> encode for EM4305/4469\n"
180 "lf gallagher clone --rc 0 --fc 9876 --cn 1234 --il 1 -> encode for T55x7 tag from decoded data"
185 arg_str0("r", "raw", "<hex>", "raw hex data. 12 bytes max"),
186 arg_lit0(NULL
, "q5", "optional - specify writing to Q5/T5555 tag"),
187 arg_lit0(NULL
, "em", "optional - specify writing to EM4305/4469 tag"),
188 arg_u64_0(NULL
, "rc", "<decimal>", "Region code. 4 bits max"),
189 arg_u64_0(NULL
, "fc", "<decimal>", "Facility code. 2 bytes max"),
190 arg_u64_0(NULL
, "cn", "<decimal>", "Card number. 3 bytes max"),
191 arg_u64_0(NULL
, "il", "<decimal>", "Issue level. 4 bits max"),
194 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
197 // skip first block, 3*4 = 12 bytes left
198 uint8_t raw
[12] = {0};
199 int res
= CLIParamHexToBuf(arg_get_str(ctx
, 1), raw
, sizeof raw
, &raw_len
);
205 bool q5
= arg_get_lit(ctx
, 2);
206 bool em
= arg_get_lit(ctx
, 3);
207 uint64_t region_code
= arg_get_u64_def(ctx
, 4, -1); // uint4, input will be validated later
208 uint64_t facility_code
= arg_get_u64_def(ctx
, 5, -1); // uint16
209 uint64_t card_number
= arg_get_u64_def(ctx
, 6, -1); // uint24
210 uint64_t issue_level
= arg_get_u64_def(ctx
, 7, -1); // uint4
214 PrintAndLogEx(FAILED
, "Can't specify both Q5 and EM4305 at the same time");
218 bool use_raw
= (raw_len
> 0);
220 if (region_code
== -1 && facility_code
== -1 && card_number
== -1 && issue_level
== -1) {
221 if (use_raw
== false) {
222 PrintAndLogEx(FAILED
, "Must specify either raw data to clone, or rc/fc/cn/il");
226 // --raw and --rc/fc/cn/il are mutually exclusive
228 PrintAndLogEx(FAILED
, "Can't specify both raw and rc/fc/cn/il at the same time");
231 if (gallagher_is_valid_creds(region_code
, facility_code
, card_number
, issue_level
) == false) {
238 for (uint8_t i
= 1; i
< ARRAYLEN(blocks
); i
++) {
239 blocks
[i
] = bytes_to_num(raw
+ ((i
- 1) * 4), sizeof(uint32_t));
242 GallagherCredentials_t creds
= {
243 .region_code
= region_code
,
244 .facility_code
= facility_code
,
245 .card_number
= card_number
,
246 .issue_level
= issue_level
,
248 // fill blocks 1 to 3 with Gallagher data
249 createBlocks(blocks
+ 1, &creds
);
252 //Pac - compat mode, NRZ, data rate 40, 3 data blocks
253 blocks
[0] = T55x7_MODULATION_MANCHESTER
| T55x7_BITRATE_RF_32
| 3 << T55x7_MAXBLOCK_SHIFT
;
254 char cardtype
[16] = {"T55x7"};
258 blocks
[0] = T5555_FIXED
| T5555_MODULATION_MANCHESTER
| T5555_SET_BITRATE(32) | 3 << T5555_MAXBLOCK_SHIFT
;
259 snprintf(cardtype
, sizeof(cardtype
), "Q5/T5555");
264 blocks
[0] = EM4305_GALLAGHER_CONFIG_BLOCK
;
265 snprintf(cardtype
, sizeof(cardtype
), "EM4305/4469");
268 PrintAndLogEx(INFO
, "Preparing to clone Gallagher to " _YELLOW_("%s") " from %s.",
270 use_raw
? "raw hex" : "specified data"
272 print_blocks(blocks
, ARRAYLEN(blocks
));
275 res
= em4x05_clone_tag(blocks
, ARRAYLEN(blocks
), 0, false);
277 res
= clone_t55xx_tag(blocks
, ARRAYLEN(blocks
));
279 PrintAndLogEx(SUCCESS
, "Done");
280 PrintAndLogEx(HINT
, "Hint: try " _YELLOW_("`lf gallagher reader`") " to verify");
284 static int CmdGallagherSim(const char *Cmd
) {
286 CLIParserContext
*ctx
;
287 CLIParserInit(&ctx
, "lf gallagher sim",
288 "Enables simulation of GALLAGHER card with specified card number.\n"
289 "Simulation runs until the button is pressed or another USB command is issued.\n",
290 "lf gallagher sim --raw 0FFD5461A9DA1346B2D1AC32\n"
291 "lf gallagher sim --rc 0 --fc 9876 --cn 1234 --il 1"
296 arg_str0("r", "raw", "<hex>", "raw hex data. 12 bytes max"),
297 arg_u64_0(NULL
, "rc", "<decimal>", "Region code. 4 bits max"),
298 arg_u64_0(NULL
, "fc", "<decimal>", "Facility code. 2 bytes max"),
299 arg_u64_0(NULL
, "cn", "<decimal>", "Card number. 3 bytes max"),
300 arg_u64_0(NULL
, "il", "<decimal>", "Issue level. 4 bits max"),
303 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
306 // skip first block, 3*4 = 12 bytes left
307 uint8_t raw
[12] = {0};
308 CLIGetHexWithReturn(ctx
, 1, raw
, &raw_len
);
309 int res
= CLIParamHexToBuf(arg_get_str(ctx
, 1), raw
, sizeof raw
, &raw_len
);
315 uint64_t region_code
= arg_get_u64_def(ctx
, 2, -1); // uint4, input will be validated later
316 uint64_t facility_code
= arg_get_u64_def(ctx
, 3, -1); // uint16
317 uint64_t card_number
= arg_get_u64_def(ctx
, 4, -1); // uint24
318 uint64_t issue_level
= arg_get_u64_def(ctx
, 5, -1); // uint4
321 bool use_raw
= raw_len
> 0;
323 if (region_code
== -1 && facility_code
== -1 && card_number
== -1 && issue_level
== -1) {
324 if (use_raw
== false) {
325 PrintAndLogEx(FAILED
, "Must specify either raw data to clone, or rc/fc/cn/il");
329 // --raw and --rc/fc/cn/il are mutually exclusive
331 PrintAndLogEx(FAILED
, "Can't specify both raw and rc/fc/cn/il at the same time");
334 if (gallagher_is_valid_creds(region_code
, facility_code
, card_number
, issue_level
) == false) {
339 if (use_raw
== false) {
340 // generate Gallagher data
341 GallagherCredentials_t creds
= {
342 .region_code
= region_code
,
343 .facility_code
= facility_code
,
344 .card_number
= card_number
,
345 .issue_level
= issue_level
,
348 createBlocks(blocks
, &creds
);
350 // convert to the normal 'raw' format
351 for (int i
= 0; i
< ARRAYLEN(blocks
); i
++) {
352 raw
[(4 * i
) + 0] = (blocks
[i
] >> 24) & 0xff;
353 raw
[(4 * i
) + 1] = (blocks
[i
] >> 16) & 0xff;
354 raw
[(4 * i
) + 2] = (blocks
[i
] >> 8) & 0xff;
355 raw
[(4 * i
) + 3] = (blocks
[i
]) & 0xff;
360 PrintAndLogEx(SUCCESS
, "Simulating Gallagher - raw " _YELLOW_("%s"), sprint_hex_inrow(raw
, sizeof(raw
)));
362 uint8_t bs
[sizeof(raw
) * 8];
363 bytes_to_bytebits(raw
, sizeof(raw
), bs
);
365 lf_asksim_t
*payload
= calloc(1, sizeof(lf_asksim_t
) + sizeof(bs
));
366 payload
->encoding
= 1;
368 payload
->separator
= 0;
370 memcpy(payload
->data
, bs
, sizeof(bs
));
372 clearCommandBuffer();
373 SendCommandNG(CMD_LF_ASK_SIMULATE
, (uint8_t *)payload
, sizeof(lf_asksim_t
) + sizeof(bs
));
376 return lfsim_wait_check(CMD_LF_ASK_SIMULATE
);
379 static command_t CommandTable
[] = {
380 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
381 {"demod", CmdGallagherDemod
, AlwaysAvailable
, "demodulate an GALLAGHER tag from the GraphBuffer"},
382 {"reader", CmdGallagherReader
, IfPm3Lf
, "attempt to read and extract tag data"},
383 {"clone", CmdGallagherClone
, IfPm3Lf
, "clone GALLAGHER tag to T55x7, Q5/T5555 or EM4305/4469"},
384 {"sim", CmdGallagherSim
, IfPm3Lf
, "simulate GALLAGHER tag"},
385 {NULL
, NULL
, NULL
, NULL
}
388 static int CmdHelp(const char *Cmd
) {
389 (void)Cmd
; // Cmd is not used so far
390 CmdsHelp(CommandTable
);
394 int CmdLFGallagher(const char *Cmd
) {
395 clearCommandBuffer();
396 return CmdsParse(CommandTable
, Cmd
);
399 // find Gallagher preamble in already demoded data
400 int detectGallagher(uint8_t *dest
, size_t *size
) {
401 if (*size
< 96) return -1; //make sure buffer has data
403 uint8_t preamble
[] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 };
404 if (!preambleSearch(dest
, preamble
, sizeof(preamble
), size
, &startIdx
))
405 return -2; //preamble not found
407 if (*size
!= 96) return -3; //wrong demoded size
408 //return start position
409 return (int)startIdx
;