]> git.zerfleddert.de Git - proxmark3-svn/blob - client/reveng/cli.c
add reveng-1.30
[proxmark3-svn] / client / reveng / cli.c
1 /* cli.c
2 * Greg Cook, 9/Apr/2015
3 */
4
5 /* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder
6 * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook
7 *
8 * This file is part of CRC RevEng.
9 *
10 * CRC RevEng is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * CRC RevEng is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with CRC RevEng. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /* 2015-04-03: added -z
25 * 2013-09-16: do not search with -M
26 * 2013-06-11: uprog() suppresses first progress report
27 * 2013-04-22: uprog() prints poly same as mtostr()
28 * 2013-02-07: added -q, uprog(), removed -W, R_ODDLY
29 * 2012-05-24: -D dumps parameters of all models
30 * 2012-03-03: added code to test sort order of model table
31 * 2012-02-20: set stdin to binary (MinGW). offer -D if preset unknown.
32 * 2011-09-06: -s reads arguments once. stdin not closed.
33 * 2011-09-06: fixed bad argument-freeing loops.
34 * 2011-08-27: validates BMP_C()
35 * 2011-08-26: validates BMPBIT and BMPSUB
36 * 2011-08-25: fixed Init/Xorout reflection logic in -V and -v
37 * 2011-01-17: fixed ANSI C warnings
38 * 2011-01-15: added NOFORCE
39 * 2011-01-14: added -k, -P
40 * 2011-01-10: reorganised switches, added -V, -X
41 * 2010-12-26: renamed CRC RevEng
42 * 2010-12-18: implemented -c, -C
43 * 2010-12-14: added and implemented -d, -D, fixed -ipx entry
44 * 2010-12-11: implemented -e. first tests
45 * 2010-12-10: finalised option processing. started input validation
46 * 2010-12-07: started cli
47 */
48
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include "getopt.h"
52 #ifdef _WIN32
53 # include <io.h>
54 # include <fcntl.h>
55 # ifndef STDIN_FILENO
56 # define STDIN_FILENO 0
57 # endif /* STDIN_FILENO */
58 #endif /* _WIN32 */
59
60 #include "reveng.h"
61
62 static FILE *oread(const char *);
63 static poly_t rdpoly(const char *, int, int);
64 static void usage(void);
65
66 static const char *myname = "reveng"; /* name of our program */
67
68 int reveng_main(int argc, char *argv[]) {
69 /* Command-line interface for CRC RevEng.
70 * Process options and switches in the argument list and
71 * run the required function.
72 */
73
74 /* default values */
75 model_t model = {
76 PZERO, /* no CRC polynomial, user must specify */
77 PZERO, /* Init = 0 */
78 P_BE, /* RefIn = false, RefOut = false, plus P_RTJUST setting in reveng.h */
79 PZERO, /* XorOut = 0 */
80 PZERO, /* check value unused */
81 NULL /* no model name */
82 };
83 int ibperhx = 8, obperhx = 8;
84 int rflags = 0, uflags = 0; /* search and UI flags */
85
86 unsigned long width = 0UL;
87 int c, mode = 0, args, psets, pass;
88 poly_t apoly, crc, qpoly = PZERO, *apolys, *pptr = NULL, *qptr = NULL;
89 model_t pset = model, *candmods, *mptr;
90 char *string;
91
92 //myname = argv[0];
93
94 /* stdin must be binary */
95 #ifdef _WIN32
96 _setmode(STDIN_FILENO, _O_BINARY);
97 #endif /* _WIN32 */
98
99 SETBMP();
100
101 do {
102 c=getopt(argc, argv, "?A:BDFLMP:SVXa:bcdefhi:k:lm:p:q:rstuvw:x:yz");
103 switch(c) {
104 case 'A': /* A: bits per output character */
105 case 'a': /* a: bits per character */
106 if((obperhx = atoi(optarg)) > BMP_BIT) {
107 fprintf(stderr,"%s: argument to -%c must be between 1 and %d\n", myname, c, BMP_BIT);
108 return 0;
109 //exit(EXIT_FAILURE);
110 }
111 if(c == 'a') ibperhx = obperhx;
112 break;
113 case 'b': /* b big-endian (RefIn = false, RefOut = false ) */
114 model.flags &= ~P_REFIN;
115 rflags |= R_HAVERI;
116 /* fall through: */
117 case 'B': /* B big-endian output (RefOut = false) */
118 model.flags &= ~P_REFOUT;
119 rflags |= R_HAVERO;
120 mnovel(&model);
121 /* fall through: */
122 case 'r': /* r right-justified */
123 model.flags |= P_RTJUST;
124 break;
125 case 'c': /* c calculate CRC */
126 case 'D': /* D list primary model names */
127 case 'd': /* d dump CRC model */
128 case 'e': /* e echo arguments */
129 case 's': /* s search for algorithm */
130 case 'v': /* v calculate reversed CRC */
131 if(mode) {
132 fprintf(stderr,"%s: more than one mode switch specified. Use %s -h for help.\n", myname, myname);
133 return 0;
134 //exit(EXIT_FAILURE);
135 }
136 mode = c;
137 break;
138 case 'F': /* F force search */
139 #ifndef NOFORCE
140 uflags |= C_FORCE;
141 #endif
142 break;
143 case 'f': /* f arguments are filenames */
144 uflags |= C_INFILE;
145 break;
146 case 'h': /* h get help / usage */
147 case 'u': /* u get help / usage */
148 case '?': /* ? get help / usage */
149 default:
150 usage();
151 return 0;
152 //exit(EXIT_FAILURE);
153 break;
154 case 'i': /* i: Init value */
155 pptr = &model.init;
156 rflags |= R_HAVEI;
157 goto ippx;
158 case 'k': /* k: polynomial in Koopman notation */
159 pfree(&model.spoly);
160 model.spoly = strtop(optarg, 0, 4);
161 pkchop(&model.spoly);
162 width = plen(model.spoly);
163 rflags |= R_HAVEP;
164 mnovel(&model);
165 break;
166 case 'l': /* l little-endian input and output */
167 model.flags |= P_REFIN;
168 rflags |= R_HAVERI;
169 /* fall through: */
170 case 'L': /* L little-endian output */
171 model.flags |= P_REFOUT;
172 rflags |= R_HAVERO;
173 mnovel(&model);
174 /* fall through: */
175 case 't': /* t left-justified */
176 model.flags &= ~P_RTJUST;
177 break;
178 case 'm': /* m: select preset CRC model */
179 if(!(c = mbynam(&model, optarg))) {
180 fprintf(stderr,"%s: preset model '%s' not found. Use %s -D to list presets.\n", myname, optarg, myname);
181 return 0;
182 //exit(EXIT_FAILURE);
183 }
184 if(c < 0)
185 uerror("no preset models available");
186 /* must set width so that parameter to -ipx is not zeroed */
187 width = plen(model.spoly);
188 rflags |= R_HAVEP | R_HAVEI | R_HAVERI | R_HAVERO | R_HAVEX;
189 break;
190 case 'M': /* M non-augmenting algorithm */
191 model.flags &= ~P_MULXN;
192 break;
193 case 'P': /* P: reversed polynomial */
194 case 'p': /* p: polynomial */
195 pptr = &model.spoly;
196 rflags &= ~R_HAVEQ;
197 rflags |= R_HAVEP;
198 ippx:
199 pfree(pptr);
200 *pptr = strtop(optarg, 0, 4);
201 pright(pptr, width);
202 if(c == 'P')
203 prev(pptr);
204 mnovel(&model);
205 break;
206 case 'q': /* q: range end polynomial */
207 pptr = &qpoly;
208 rflags &= ~R_HAVEP;
209 rflags |= R_HAVEQ;
210 goto ippx;
211 case 'S': /* s space between output characters */
212 model.flags |= P_SPACE;
213 break;
214 case 'V': /* v reverse algorithm */
215 /* Distinct from the -v switch as the
216 * user will have to reverse his or her
217 * own arguments. The user cannot dump
218 * the model generated by -v either.
219 */
220 mrev(&model);
221 break;
222 case 'w': /* w: CRC width = order - 1 */
223 width = (unsigned long) atol(optarg);
224 break;
225 case 'X': /* X print uppercase hex */
226 model.flags |= P_UPPER;
227 break;
228 case 'x': /* x: XorOut value */
229 pptr = &model.xorout;
230 rflags |= R_HAVEX;
231 goto ippx;
232 case 'y': /* y little-endian byte order in files */
233 model.flags |= P_LTLBYT;
234 break;
235 case 'z': /* z raw binary arguments */
236 model.flags |= P_DIRECT;
237 break;
238 case -1: /* no more options, continue */
239 ;
240 }
241 } while(c != -1);
242
243 /* canonicalise the model, so the one we dump is the one we
244 * calculate with (not with -s, spoly may be blank which will
245 * normalise to zero and clear init and xorout.)
246 */
247 if(mode != 's')
248 mcanon(&model);
249
250 switch(mode) {
251 case 'v': /* v calculate reversed CRC */
252 /* Distinct from the -V switch as this causes
253 * the arguments and output to be reversed as well.
254 */
255 /* reciprocate Poly */
256 prcp(&model.spoly);
257
258 /* mrev() does:
259 * if(refout) prev(init); else prev(xorout);
260 * but here the entire argument polynomial is
261 * reflected, not just the characters, so RefIn
262 * and RefOut are not inverted as with -V.
263 * Consequently Init is the mirror image of the
264 * one resulting from -V, and so we have:
265 */
266 if(~model.flags & P_REFOUT) {
267 prev(&model.init);
268 prev(&model.xorout);
269 }
270
271 /* swap init and xorout */
272 apoly = model.init;
273 model.init = model.xorout;
274 model.xorout = apoly;
275
276 /* fall through: */
277 case 'c': /* c calculate CRC */
278
279 /* validate inputs */
280 /* if(plen(model.spoly) == 0) {
281 * fprintf(stderr,"%s: no polynomial specified for -%c (add -w WIDTH -p POLY)\n", myname, mode);
282 * exit(EXIT_FAILURE);
283 * }
284 */
285
286 /* in the Williams model, xorout is applied after the refout stage.
287 * as refout is part of ptostr(), we reverse xorout here.
288 */
289 if(model.flags & P_REFOUT)
290 prev(&model.xorout);
291
292 for(; optind < argc; ++optind) {
293 if(uflags & C_INFILE)
294 apoly = rdpoly(argv[optind], model.flags, ibperhx);
295 else
296 apoly = strtop(argv[optind], model.flags, ibperhx);
297
298 if(mode == 'v')
299 prev(&apoly);
300
301 crc = pcrc(apoly, model.spoly, model.init, model.xorout, model.flags);
302
303 if(mode == 'v')
304 prev(&crc);
305
306 string = ptostr(crc, model.flags, obperhx);
307 puts(string);
308 free(string);
309 pfree(&crc);
310 pfree(&apoly);
311 }
312 break;
313 case 'D': /* D dump all models */
314 args = mcount();
315 if(!args)
316 uerror("no preset models available");
317 for(mode = 0; mode < args; ++mode) {
318 mbynum(&model, mode);
319 mcanon(&model);
320 ufound(&model);
321 }
322 break;
323 case 'd': /* d dump CRC model */
324 /* maybe we don't want to do this:
325 * either attaching names to arbitrary models or forcing to a preset
326 * mmatch(&model, M_OVERWR);
327 */
328 if(~model.flags & P_MULXN)
329 uerror("not a Williams model compliant algorithm");
330 string = mtostr(&model);
331 puts(string);
332 free(string);
333 break;
334 case 'e': /* e echo arguments */
335 for(; optind < argc; ++optind) {
336 if(uflags & C_INFILE)
337 apoly = rdpoly(argv[optind], model.flags, ibperhx);
338 else
339 apoly = strtop(argv[optind], model.flags, ibperhx);
340
341 psum(&apoly, model.init, 0UL);
342 string = ptostr(apoly, model.flags, obperhx);
343 puts(string);
344 free(string);
345 pfree(&apoly);
346 }
347 break;
348 case 's': /* s search for algorithm */
349 if(!width)
350 uerror("must specify positive -k or -w before -s");
351 if(~model.flags & P_MULXN)
352 uerror("cannot search for non-Williams compliant models");
353 praloc(&model.spoly, width);
354 praloc(&model.init, width);
355 praloc(&model.xorout, width);
356 if(!plen(model.spoly))
357 palloc(&model.spoly, width);
358 else
359 width = plen(model.spoly);
360
361 /* special case if qpoly is zero, search to end of range */
362 if(!ptst(qpoly))
363 rflags &= ~R_HAVEQ;
364
365 /* allocate argument array */
366 args = argc - optind;
367 if(!(apolys = malloc(args * sizeof(poly_t))))
368 uerror("cannot allocate memory for argument list");
369
370 for(pptr = apolys; optind < argc; ++optind) {
371 if(uflags & C_INFILE)
372 *pptr++ = rdpoly(argv[optind], model.flags, ibperhx);
373 else
374 *pptr++ = strtop(argv[optind], model.flags, ibperhx);
375 }
376 /* exit value of pptr is used hereafter! */
377
378 /* if endianness not specified, try
379 * little-endian then big-endian.
380 * NB: crossed-endian algorithms will not be
381 * searched.
382 */
383
384 /* scan against preset models */
385 if(~uflags & C_FORCE) {
386 pass = 0;
387 do {
388 psets = mcount();
389 while(psets) {
390 mbynum(&pset, --psets);
391 /* skip if different width, or refin or refout don't match */
392 if(plen(pset.spoly) != width || (model.flags ^ pset.flags) & (P_REFIN | P_REFOUT))
393 continue;
394 /* skip if the preset doesn't match specified parameters */
395 if(rflags & R_HAVEP && pcmp(&model.spoly, &pset.spoly))
396 continue;
397 if(rflags & R_HAVEI && psncmp(&model.init, &pset.init))
398 continue;
399 if(rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout))
400 continue;
401 apoly = pclone(pset.xorout);
402 if(pset.flags & P_REFOUT)
403 prev(&apoly);
404 for(qptr = apolys; qptr < pptr; ++qptr) {
405 crc = pcrc(*qptr, pset.spoly, pset.init, apoly, 0);
406 if(ptst(crc)) {
407 pfree(&crc);
408 break;
409 } else
410 pfree(&crc);
411 }
412 pfree(&apoly);
413 if(qptr == pptr) {
414 /* the selected model solved all arguments */
415 mcanon(&pset);
416 ufound(&pset);
417 uflags |= C_RESULT;
418 }
419 }
420 mfree(&pset);
421
422 /* toggle refIn/refOut and reflect arguments */
423 if(~rflags & R_HAVERI) {
424 model.flags ^= P_REFIN | P_REFOUT;
425 for(qptr = apolys; qptr < pptr; ++qptr)
426 prevch(qptr, ibperhx);
427 }
428 } while(~rflags & R_HAVERI && ++pass < 2);
429 }
430 if(uflags & C_RESULT) {
431 for(qptr = apolys; qptr < pptr; ++qptr)
432 pfree(qptr);
433 return 1;
434 //exit(EXIT_SUCCESS);
435 }
436 if(!(model.flags & P_REFIN) != !(model.flags & P_REFOUT))
437 uerror("cannot search for crossed-endian models");
438 pass = 0;
439 do {
440 mptr = candmods = reveng(&model, qpoly, rflags, args, apolys);
441 if(mptr && plen(mptr->spoly))
442 uflags |= C_RESULT;
443 while(mptr && plen(mptr->spoly)) {
444 /* results were printed by the callback
445 * string = mtostr(mptr);
446 * puts(string);
447 * free(string);
448 */
449 mfree(mptr++);
450 }
451 free(candmods);
452 if(~rflags & R_HAVERI) {
453 model.flags ^= P_REFIN | P_REFOUT;
454 for(qptr = apolys; qptr < pptr; ++qptr)
455 prevch(qptr, ibperhx);
456 }
457 } while(~rflags & R_HAVERI && ++pass < 2);
458 for(qptr = apolys; qptr < pptr; ++qptr)
459 pfree(qptr);
460 free(apolys);
461 if(~uflags & C_RESULT)
462 uerror("no models found");
463 break;
464 default: /* no mode specified */
465 fprintf(stderr, "%s: no mode switch specified. Use %s -h for help.\n", myname, myname);
466 return 0;
467 //exit(EXIT_FAILURE);
468 }
469
470 return 1;
471 //exit(EXIT_SUCCESS);
472 }
473
474 void
475 ufound(const model_t *model) {
476 /* Callback function to report each model found */
477 char *string;
478
479 if(!model) return;
480 /* generated models will be canonical */
481 string = mtostr(model);
482 puts(string);
483 free(string);
484 }
485
486 void
487 uerror(const char *msg) {
488 /* Callback function to report fatal errors */
489 fprintf(stderr, "%s: %s\n", myname, msg);
490 exit(EXIT_FAILURE);
491 }
492
493 void
494 uprog(const poly_t gpoly, int flags, unsigned long seq) {
495 /* Callback function to report search progress */
496 char *string;
497
498 /* Suppress first report in CLI */
499 if(!seq)
500 return;
501 string = ptostr(gpoly, P_RTJUST, 4);
502 fprintf(stderr, "%s: searching: width=%ld poly=0x%s refin=%s refout=%s\n",
503 myname, plen(gpoly), string,
504 (flags & P_REFIN ? "true" : "false"),
505 (flags & P_REFOUT ? "true" : "false")
506 );
507 free(string);
508 }
509
510 static poly_t
511 rdpoly(const char *name, int flags, int bperhx) {
512 /* read poly from file in chunks and report errors */
513
514 poly_t apoly = PZERO, chunk = PZERO;
515 FILE *input;
516
517 input = oread(name);
518 while(!feof(input) && !ferror(input)) {
519 chunk = filtop(input, BUFFER, flags, bperhx);
520 psum(&apoly, chunk, plen(apoly));
521 pfree(&chunk);
522 }
523 if(ferror(input)) {
524 fprintf(stderr,"%s: error condition on file '%s'\n", myname, name);
525 exit(EXIT_FAILURE);
526 }
527 /* close file unless stdin */
528 if(input == stdin)
529 /* reset EOF condition */
530 clearerr(input);
531 else if(fclose(input)) {
532 fprintf(stderr,"%s: error closing file '%s'\n", myname, name);
533 exit(EXIT_FAILURE);
534 }
535 return(apoly);
536 }
537
538 static FILE *
539 oread(const char *name) {
540 /* open file for reading and report errors */
541 FILE *handle;
542
543 /* recognise special name '-' as standard input */
544 if(*name == '-' && name[1] == '\0')
545 return(stdin);
546 if(!(handle = fopen(name, "rb"))) {
547 fprintf(stderr, "%s: cannot open '%s' for reading\n", myname, name);
548 exit(EXIT_FAILURE);
549 }
550 return(handle);
551 }
552
553 static void
554 usage(void) {
555 /* print usage if asked, or if syntax incorrect */
556 fprintf(stderr,
557 "CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder\n"
558 "Usage:\t");
559 fputs(myname, stderr);
560 fprintf(stderr,
561 "\t-cdDesvhu? [-bBfFlLMrStVXyz]\n"
562 "\t\t[-a BITS] [-A OBITS] [-i INIT] [-k KPOLY] [-m MODEL]\n"
563 "\t\t[-p POLY] [-P RPOLY] [-q QPOLY] [-w WIDTH] [-x XOROUT]\n"
564 "\t\t[STRING...]\n"
565 "Options:\n"
566 "\t-a BITS\t\tbits per character (1 to %d)\n"
567 "\t-A OBITS\tbits per output character (1 to %d)\n"
568 "\t-i INIT\t\tinitial register value\n"
569 "\t-k KPOLY\tgenerator in Koopman notation (implies WIDTH)\n"
570 "\t-m MODEL\tpreset CRC algorithm\n"
571 "\t-p POLY\t\tgenerator or search range start polynomial\n"
572 "\t-P RPOLY\treversed generator polynomial\n",
573 BMP_BIT, BMP_BIT);
574 fprintf(stderr,
575 "\t-q QPOLY\tsearch range end polynomial\n"
576 "\t-w WIDTH\tregister size, in bits\n"
577 "\t-x XOROUT\tfinal register XOR value\n"
578 "Modifier switches:\n"
579 "\t-b big-endian CRC\t\t-B big-endian CRC output\n"
580 "\t-f read files named in STRINGs\t-F find presets less quickly\n"
581 "\t-l little-endian CRC\t\t-L little-endian CRC output\n"
582 "\t-M non-augmenting algorithm\t-r right-justified output\n"
583 "\t-S print spaces between chars\t-t left-justified output\n"
584 "\t-V reverse algorithm only\t-X print uppercase hex\n"
585 "\t-y low bytes first in files\t-z raw binary STRINGs\n");
586 fprintf(stderr,
587 "Mode switches:\n"
588 "\t-c calculate CRCs\t\t-d dump algorithm parameters\n"
589 "\t-D list preset algorithms\t-e echo (and reformat) input\n"
590 "\t-s search for algorithm\t\t-v calculate reversed CRCs\n"
591 "\t-h | -u | -? show this help\n"
592 "\n"
593 "Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Gregory Cook\n"
594 "This is free software; see the source for copying conditions. There is NO\n"
595 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
596 "Version "
597 VERSION
598 "\t\t\t\t <http://reveng.sourceforge.net/>\n");
599 }
Impressum, Datenschutz