]>
Commit | Line | Data |
---|---|---|
cee5a30d | 1 | //----------------------------------------------------------------------------- |
2 | // Copyright (C) 2010 iZsh <izsh at fail0verflow.com>, Hagen Fritsch | |
3 | // Copyright (C) 2011 Gerhard de Koning Gans | |
4 | // | |
5 | // This code is licensed to you under the terms of the GNU GPL, version 2 or, | |
6 | // at your option, any later version. See the LICENSE.txt file for the text of | |
7 | // the license. | |
8 | //----------------------------------------------------------------------------- | |
9 | // High frequency iClass commands | |
10 | //----------------------------------------------------------------------------- | |
11 | ||
12 | #include <stdio.h> | |
13 | #include <stdlib.h> | |
14 | #include <string.h> | |
9f6e9d15 | 15 | #include <sys/stat.h> |
cee5a30d | 16 | #include "iso14443crc.h" // Can also be used for iClass, using 0xE012 as CRC-type |
17 | #include "data.h" | |
28fdb04f | 18 | //#include "proxusb.h" |
902cb3c0 | 19 | #include "proxmark3.h" |
cee5a30d | 20 | #include "ui.h" |
21 | #include "cmdparser.h" | |
22 | #include "cmdhficlass.h" | |
23 | #include "common.h" | |
14006804 | 24 | #include "util.h" |
17cba269 | 25 | #include "cmdmain.h" |
cee5a30d | 26 | |
27 | static int CmdHelp(const char *Cmd); | |
28 | ||
17cba269 MHS |
29 | int xorbits_8(uint8_t val) |
30 | { | |
31 | uint8_t res = val ^ (val >> 1); //1st pass | |
32 | res = res ^ (res >> 1); // 2nd pass | |
33 | res = res ^ (res >> 2); // 3rd pass | |
34 | res = res ^ (res >> 4); // 4th pass | |
35 | return res & 1; | |
36 | } | |
37 | ||
cee5a30d | 38 | int CmdHFiClassList(const char *Cmd) |
17cba269 MHS |
39 | { |
40 | ||
41 | bool ShowWaitCycles = false; | |
42 | char param = param_getchar(Cmd, 0); | |
43 | ||
44 | if (param != 0) { | |
45 | PrintAndLog("List data in trace buffer."); | |
46 | PrintAndLog("Usage: hf iclass list"); | |
47 | PrintAndLog("h - help"); | |
48 | PrintAndLog("sample: hf iclass list"); | |
49 | return 0; | |
50 | } | |
51 | ||
52 | uint8_t got[1920]; | |
53 | GetFromBigBuf(got,sizeof(got),0); | |
54 | WaitForResponse(CMD_ACK,NULL); | |
55 | ||
56 | PrintAndLog("Recorded Activity"); | |
57 | PrintAndLog(""); | |
58 | PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); | |
59 | PrintAndLog("All times are in carrier periods (1/13.56Mhz)"); | |
60 | PrintAndLog(""); | |
61 | PrintAndLog(" Start | End | Src | Data"); | |
62 | PrintAndLog("-----------|-----------|-----|--------"); | |
63 | ||
64 | int i; | |
65 | uint32_t first_timestamp = 0; | |
66 | uint32_t timestamp; | |
67 | bool tagToReader; | |
68 | uint32_t parityBits; | |
69 | uint8_t len; | |
70 | uint8_t *frame; | |
71 | uint32_t EndOfTransmissionTimestamp = 0; | |
72 | ||
17cba269 MHS |
73 | |
74 | for( i=0; i < 1900;) | |
75 | { | |
76 | //First 32 bits contain | |
77 | // isResponse (1 bit) | |
78 | // timestamp (remaining) | |
79 | //Then paritybits | |
80 | //Then length | |
81 | timestamp = *((uint32_t *)(got+i)); | |
82 | parityBits = *((uint32_t *)(got+i+4)); | |
83 | len = got[i+8]; | |
84 | frame = (got+i+9); | |
85 | uint32_t next_timestamp = (*((uint32_t *)(got+i+9))) & 0x7fffffff; | |
86 | ||
87 | tagToReader = timestamp & 0x80000000; | |
88 | timestamp &= 0x7fffffff; | |
89 | ||
90 | if(i==0) { | |
91 | first_timestamp = timestamp; | |
92 | } | |
93 | ||
f83cc126 MHS |
94 | // Break and stick with current result if buffer was not completely full |
95 | if (frame[0] == 0x44 && frame[1] == 0x44 && frame[2] == 0x44 && frame[3] == 0x44) break; | |
17cba269 MHS |
96 | |
97 | char line[1000] = ""; | |
98 | ||
99 | if(len)//We have some data to display | |
100 | { | |
101 | int j,oddparity; | |
102 | ||
103 | for(j = 0; j < len ; j++) | |
104 | { | |
105 | oddparity = 0x01 ^ xorbits_8(frame[j] & 0xFF); | |
106 | ||
107 | if (tagToReader && (oddparity != ((parityBits >> (len - j - 1)) & 0x01))) { | |
108 | sprintf(line+(j*4), "%02x! ", frame[j]); | |
109 | } else { | |
110 | sprintf(line+(j*4), "%02x ", frame[j]); | |
111 | } | |
112 | } | |
113 | }else | |
114 | { | |
115 | if (ShowWaitCycles) { | |
116 | sprintf(line, "fdt (Frame Delay Time): %d", (next_timestamp - timestamp)); | |
117 | } | |
118 | } | |
119 | ||
120 | char *crc = ""; | |
121 | ||
122 | if(len > 2) | |
123 | { | |
124 | uint8_t b1, b2; | |
125 | if(!tagToReader && len == 4) { | |
126 | // Rough guess that this is a command from the reader | |
127 | // For iClass the command byte is not part of the CRC | |
128 | ComputeCrc14443(CRC_ICLASS, &frame[1], len-3, &b1, &b2); | |
129 | } | |
130 | else { | |
131 | // For other data.. CRC might not be applicable (UPDATE commands etc.) | |
132 | ComputeCrc14443(CRC_ICLASS, frame, len-2, &b1, &b2); | |
133 | } | |
134 | ||
135 | if (b1 != frame[len-2] || b2 != frame[len-1]) { | |
136 | crc = (tagToReader & (len < 8)) ? "" : " !crc"; | |
137 | } | |
138 | } | |
139 | ||
140 | i += (len + 9); | |
141 | EndOfTransmissionTimestamp = (*((uint32_t *)(got+i))) & 0x7fffffff; | |
142 | ||
143 | // Not implemented for iclass on the ARM-side | |
144 | //if (!ShowWaitCycles) i += 9; | |
145 | ||
146 | PrintAndLog(" %9d | %9d | %s | %s %s", | |
147 | (timestamp - first_timestamp), | |
148 | (EndOfTransmissionTimestamp - first_timestamp), | |
149 | (len?(tagToReader ? "Tag" : "Rdr"):" "), | |
150 | line, crc); | |
151 | } | |
152 | return 0; | |
153 | } | |
154 | ||
155 | int CmdHFiClassListOld(const char *Cmd) | |
cee5a30d | 156 | { |
157 | uint8_t got[1920]; | |
db09cb3a | 158 | GetFromBigBuf(got,sizeof(got),0); |
cee5a30d | 159 | |
160 | PrintAndLog("recorded activity:"); | |
161 | PrintAndLog(" ETU :rssi: who bytes"); | |
162 | PrintAndLog("---------+----+----+-----------"); | |
163 | ||
164 | int i = 0; | |
165 | int prev = -1; | |
166 | ||
167 | for (;;) { | |
168 | if(i >= 1900) { | |
169 | break; | |
170 | } | |
171 | ||
172 | bool isResponse; | |
173 | int timestamp = *((uint32_t *)(got+i)); | |
174 | if (timestamp & 0x80000000) { | |
175 | timestamp &= 0x7fffffff; | |
176 | isResponse = 1; | |
177 | } else { | |
178 | isResponse = 0; | |
179 | } | |
180 | ||
17cba269 | 181 | |
cee5a30d | 182 | int metric = 0; |
17cba269 | 183 | |
cee5a30d | 184 | int parityBits = *((uint32_t *)(got+i+4)); |
185 | // 4 bytes of additional information... | |
186 | // maximum of 32 additional parity bit information | |
187 | // | |
188 | // TODO: | |
189 | // at each quarter bit period we can send power level (16 levels) | |
190 | // or each half bit period in 256 levels. | |
191 | ||
192 | ||
193 | int len = got[i+8]; | |
194 | ||
195 | if (len > 100) { | |
196 | break; | |
197 | } | |
198 | if (i + len >= 1900) { | |
199 | break; | |
200 | } | |
201 | ||
202 | uint8_t *frame = (got+i+9); | |
203 | ||
204 | // Break and stick with current result if buffer was not completely full | |
205 | if (frame[0] == 0x44 && frame[1] == 0x44 && frame[3] == 0x44) { break; } | |
206 | ||
207 | char line[1000] = ""; | |
208 | int j; | |
209 | for (j = 0; j < len; j++) { | |
210 | int oddparity = 0x01; | |
211 | int k; | |
212 | ||
213 | for (k=0;k<8;k++) { | |
214 | oddparity ^= (((frame[j] & 0xFF) >> k) & 0x01); | |
215 | } | |
216 | ||
217 | //if((parityBits >> (len - j - 1)) & 0x01) { | |
218 | if (isResponse && (oddparity != ((parityBits >> (len - j - 1)) & 0x01))) { | |
219 | sprintf(line+(j*4), "%02x! ", frame[j]); | |
220 | } | |
221 | else { | |
222 | sprintf(line+(j*4), "%02x ", frame[j]); | |
223 | } | |
224 | } | |
225 | ||
226 | char *crc; | |
227 | crc = ""; | |
228 | if (len > 2) { | |
229 | uint8_t b1, b2; | |
230 | for (j = 0; j < (len - 1); j++) { | |
231 | // gives problems... search for the reason.. | |
232 | /*if(frame[j] == 0xAA) { | |
233 | switch(frame[j+1]) { | |
234 | case 0x01: | |
235 | crc = "[1] Two drops close after each other"; | |
236 | break; | |
237 | case 0x02: | |
238 | crc = "[2] Potential SOC with a drop in second half of bitperiod"; | |
239 | break; | |
240 | case 0x03: | |
241 | crc = "[3] Segment Z after segment X is not possible"; | |
242 | break; | |
243 | case 0x04: | |
244 | crc = "[4] Parity bit of a fully received byte was wrong"; | |
245 | break; | |
246 | default: | |
247 | crc = "[?] Unknown error"; | |
248 | break; | |
249 | } | |
250 | break; | |
251 | }*/ | |
252 | } | |
253 | ||
254 | if (strlen(crc)==0) { | |
255 | if(!isResponse && len == 4) { | |
256 | // Rough guess that this is a command from the reader | |
257 | // For iClass the command byte is not part of the CRC | |
258 | ComputeCrc14443(CRC_ICLASS, &frame[1], len-3, &b1, &b2); | |
259 | } | |
260 | else { | |
261 | // For other data.. CRC might not be applicable (UPDATE commands etc.) | |
262 | ComputeCrc14443(CRC_ICLASS, frame, len-2, &b1, &b2); | |
263 | } | |
264 | //printf("%1x %1x",(unsigned)b1,(unsigned)b2); | |
265 | if (b1 != frame[len-2] || b2 != frame[len-1]) { | |
266 | crc = (isResponse & (len < 8)) ? "" : " !crc"; | |
267 | } else { | |
268 | crc = ""; | |
269 | } | |
270 | } | |
271 | } else { | |
272 | crc = ""; // SHORT | |
273 | } | |
274 | ||
275 | char metricString[100]; | |
276 | if (isResponse) { | |
277 | sprintf(metricString, "%3d", metric); | |
278 | } else { | |
279 | strcpy(metricString, " "); | |
280 | } | |
281 | ||
282 | PrintAndLog(" +%7d: %s: %s %s %s", | |
283 | (prev < 0 ? 0 : (timestamp - prev)), | |
284 | metricString, | |
285 | (isResponse ? "TAG" : " "), line, crc); | |
286 | ||
287 | prev = timestamp; | |
288 | i += (len + 9); | |
289 | } | |
290 | return 0; | |
291 | } | |
292 | ||
293 | /*void iso14a_set_timeout(uint32_t timeout) { | |
294 | UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_SET_TIMEOUT, 0, timeout}}; | |
295 | SendCommand(&c); | |
296 | }*/ | |
297 | ||
298 | int CmdHFiClassSnoop(const char *Cmd) | |
299 | { | |
300 | UsbCommand c = {CMD_SNOOP_ICLASS}; | |
301 | SendCommand(&c); | |
302 | return 0; | |
303 | } | |
304 | ||
1e262141 | 305 | int CmdHFiClassSim(const char *Cmd) |
306 | { | |
307 | uint8_t simType = 0; | |
308 | uint8_t CSN[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | |
309 | ||
17cba269 MHS |
310 | if (strlen(Cmd)<1) { |
311 | PrintAndLog("Usage: hf iclass sim [0 <CSN>] | x"); | |
312 | PrintAndLog(" options"); | |
313 | PrintAndLog(" 0 <CSN> simulate the given CSN"); | |
314 | PrintAndLog(" 1 simulate default CSN"); | |
315 | PrintAndLog(" 2 iterate CSNs, gather MACs"); | |
1e262141 | 316 | PrintAndLog(" sample: hf iclass sim 0 031FEC8AF7FF12E0"); |
17cba269 | 317 | PrintAndLog(" sample: hf iclass sim 2"); |
1e262141 | 318 | return 0; |
319 | } | |
320 | ||
321 | simType = param_get8(Cmd, 0); | |
17cba269 MHS |
322 | |
323 | if(simType == 0) | |
324 | { | |
325 | if (param_gethex(Cmd, 1, CSN, 16)) { | |
326 | PrintAndLog("A CSN should consist of 16 HEX symbols"); | |
327 | return 1; | |
328 | } | |
329 | PrintAndLog("--simtype:%02x csn:%s", simType, sprint_hex(CSN, 8)); | |
330 | ||
331 | } | |
77abe781 | 332 | if(simType > 2) |
17cba269 MHS |
333 | { |
334 | PrintAndLog("Undefined simptype %d", simType); | |
335 | return 1; | |
1e262141 | 336 | } |
17cba269 | 337 | uint8_t numberOfCSNs=0; |
1e262141 | 338 | |
9f6e9d15 MHS |
339 | if(simType == 2) |
340 | { | |
341 | UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,63}}; | |
342 | UsbCommand resp = {0}; | |
17cba269 | 343 | |
77abe781 MHS |
344 | uint8_t csns[64] = { |
345 | 0x00,0x0B,0x0F,0xFF,0xF7,0xFF,0x12,0xE0 , | |
346 | 0x00,0x13,0x94,0x7e,0x76,0xff,0x12,0xe0 , | |
347 | 0x2a,0x99,0xac,0x79,0xec,0xff,0x12,0xe0 , | |
348 | 0x17,0x12,0x01,0xfd,0xf7,0xff,0x12,0xe0 , | |
349 | 0xcd,0x56,0x01,0x7c,0x6f,0xff,0x12,0xe0 , | |
350 | 0x4b,0x5e,0x0b,0x72,0xef,0xff,0x12,0xe0 , | |
351 | 0x00,0x73,0xd8,0x75,0x58,0xff,0x12,0xe0 , | |
352 | 0x0c,0x90,0x32,0xf3,0x5d,0xff,0x12,0xe0 }; | |
353 | ||
354 | memcpy(c.d.asBytes, csns, 64); | |
9f6e9d15 MHS |
355 | |
356 | SendCommand(&c); | |
357 | if (!WaitForResponseTimeout(CMD_ACK, &resp, -1)) { | |
358 | PrintAndLog("Command timed out"); | |
359 | return 0; | |
360 | } | |
1e262141 | 361 | |
77abe781 MHS |
362 | uint8_t num_mac_responses = resp.arg[1]; |
363 | PrintAndLog("Mac responses: %d MACs obtained (should be 8)", num_mac_responses); | |
9f6e9d15 | 364 | |
77abe781 | 365 | size_t datalen = 8*24; |
9f6e9d15 MHS |
366 | /* |
367 | * Now, time to dump to file. We'll use this format: | |
77abe781 | 368 | * <8-byte CSN><8-byte CC><4 byte NR><4 byte MAC>.... |
9f6e9d15 | 369 | * So, it should wind up as |
77abe781 MHS |
370 | * 8 * 24 bytes. |
371 | * | |
372 | * The returndata from the pm3 is on the following format | |
373 | * <4 byte NR><4 byte MAC> | |
374 | * CC are all zeroes, CSN is the same as was sent in | |
9f6e9d15 | 375 | **/ |
77abe781 MHS |
376 | void* dump = malloc(datalen); |
377 | memset(dump,0,datalen);//<-- Need zeroes for the CC-field | |
9f6e9d15 | 378 | uint8_t i = 0; |
77abe781 | 379 | for(i = 0 ; i < 8 ; i++) |
9f6e9d15 | 380 | { |
77abe781 MHS |
381 | memcpy(dump+i*24, csns+i*8,8); //CSN |
382 | //8 zero bytes here... | |
383 | //Then comes NR_MAC (eight bytes from the response) | |
384 | memcpy(dump+i*24+16,resp.d.asBytes+i*8,8); | |
1e262141 | 385 | |
9f6e9d15 MHS |
386 | } |
387 | /** Now, save to dumpfile **/ | |
77abe781 MHS |
388 | saveFile("iclass_mac_attack", "bin", dump,datalen); |
389 | free(dump); | |
9f6e9d15 MHS |
390 | }else |
391 | { | |
392 | UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,numberOfCSNs}}; | |
393 | memcpy(c.d.asBytes, CSN, 8); | |
394 | SendCommand(&c); | |
395 | } | |
1e262141 | 396 | return 0; |
397 | } | |
398 | ||
399 | int CmdHFiClassReader(const char *Cmd) | |
400 | { | |
401 | uint8_t readerType = 0; | |
402 | ||
403 | if (strlen(Cmd)<1) { | |
404 | PrintAndLog("Usage: hf iclass reader <reader type>"); | |
405 | PrintAndLog(" sample: hf iclass reader 0"); | |
406 | return 0; | |
407 | } | |
408 | ||
409 | readerType = param_get8(Cmd, 0); | |
410 | PrintAndLog("--readertype:%02x", readerType); | |
411 | ||
412 | UsbCommand c = {CMD_READER_ICLASS, {readerType}}; | |
413 | //memcpy(c.d.asBytes, CSN, 8); | |
414 | SendCommand(&c); | |
415 | ||
416 | /*UsbCommand * resp = WaitForResponseTimeout(CMD_ACK, 1500); | |
417 | if (resp != NULL) { | |
418 | uint8_t isOK = resp->arg[0] & 0xff; | |
419 | PrintAndLog("isOk:%02x", isOK); | |
420 | } else { | |
421 | PrintAndLog("Command execute timeout"); | |
422 | }*/ | |
423 | ||
424 | return 0; | |
425 | } | |
426 | ||
cee5a30d | 427 | static command_t CommandTable[] = |
428 | { | |
429 | {"help", CmdHelp, 1, "This help"}, | |
430 | {"list", CmdHFiClassList, 0, "List iClass history"}, | |
431 | {"snoop", CmdHFiClassSnoop, 0, "Eavesdrop iClass communication"}, | |
1e262141 | 432 | {"sim", CmdHFiClassSim, 0, "Simulate iClass tag"}, |
433 | {"reader", CmdHFiClassReader, 0, "Read an iClass tag"}, | |
cee5a30d | 434 | {NULL, NULL, 0, NULL} |
435 | }; | |
436 | ||
437 | int CmdHFiClass(const char *Cmd) | |
438 | { | |
439 | CmdsParse(CommandTable, Cmd); | |
440 | return 0; | |
441 | } | |
442 | ||
443 | int CmdHelp(const char *Cmd) | |
444 | { | |
445 | CmdsHelp(CommandTable); | |
446 | return 0; | |
447 | } | |
9f6e9d15 MHS |
448 | |
449 | /** | |
450 | * @brief checks if a file exists | |
451 | * @param filename | |
452 | * @return | |
453 | */ | |
454 | int fileExists(const char *filename) { | |
455 | struct stat st; | |
456 | int result = stat(filename, &st); | |
457 | return result == 0; | |
458 | } | |
459 | /** | |
460 | * @brief Utility function to save data to a file. This method takes a preferred name, but if that | |
461 | * file already exists, it tries with another name until it finds something suitable. | |
462 | * E.g. dumpdata-15.txt | |
463 | * @param preferredName | |
464 | * @param suffix the file suffix. Leave out the ".". | |
465 | * @param data The binary data to write to the file | |
466 | * @param datalen the length of the data | |
77abe781 | 467 | * @return 0 for ok, 1 for failz |
9f6e9d15 MHS |
468 | */ |
469 | int saveFile(const char *preferredName, const char *suffix, const void* data, size_t datalen) | |
470 | { | |
471 | FILE *f = fopen(preferredName, "wb"); | |
472 | int size = sizeof(char) * (strlen(preferredName)+strlen(suffix)+5); | |
473 | char * fileName = malloc(size); | |
474 | ||
475 | memset(fileName,0,size); | |
476 | int num = 1; | |
477 | sprintf(fileName,"%s.%s", preferredName, suffix); | |
478 | while(fileExists(fileName)) | |
479 | { | |
480 | sprintf(fileName,"%s-%d.%s", preferredName, num, suffix); | |
481 | num++; | |
482 | } | |
483 | /* We should have a valid filename now, e.g. dumpdata-3.bin */ | |
484 | ||
485 | /*Opening file for writing in binary mode*/ | |
486 | FILE *fileHandle=fopen(fileName,"wb"); | |
487 | if(!f) { | |
488 | PrintAndLog("Failed to write to file '%s'", fileName); | |
489 | return 0; | |
490 | } | |
491 | fwrite(data, 1, datalen, fileHandle); | |
492 | fclose(fileHandle); | |
493 | PrintAndLog("Saved data to '%s'", fileName); | |
494 | ||
495 | free(fileName); | |
496 | return 0; | |
497 | } |