]> cvs.zerfleddert.de Git - proxmark3-svn/blame - client/reveng/cli.c
FIX: Mental note to self, bitnumber 4 is used, not the value 4 which would be bitnum...
[proxmark3-svn] / client / reveng / cli.c
CommitLineData
a71ece51 1/* cli.c
a78a3d9d 2 * Greg Cook, 24/Feb/2016
a71ece51 3 */
4
5/* CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder
a78a3d9d 6 * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016 Gregory Cook
a71ece51 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
62static FILE *oread(const char *);
63static poly_t rdpoly(const char *, int, int);
64static void usage(void);
65
66static const char *myname = "reveng"; /* name of our program */
67
68int 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 pos=0;
102 optind=1;
103 do {
104 c=getopt(argc, argv, "?A:BDFLMP:SVXa:bcdefhi:k:lm:p:q:rstuvw:x:yz");
105 switch(c) {
106 case 'A': /* A: bits per output character */
107 case 'a': /* a: bits per character */
108 if((obperhx = atoi(optarg)) > BMP_BIT) {
109 fprintf(stderr,"%s: argument to -%c must be between 1 and %d\n", myname, c, BMP_BIT);
110 return 0;
111 //exit(EXIT_FAILURE);
112 }
113 if(c == 'a') ibperhx = obperhx;
114 break;
115 case 'b': /* b big-endian (RefIn = false, RefOut = false ) */
116 model.flags &= ~P_REFIN;
117 rflags |= R_HAVERI;
118 /* fall through: */
119 case 'B': /* B big-endian output (RefOut = false) */
120 model.flags &= ~P_REFOUT;
121 rflags |= R_HAVERO;
122 mnovel(&model);
123 /* fall through: */
124 case 'r': /* r right-justified */
125 model.flags |= P_RTJUST;
126 break;
127 case 'c': /* c calculate CRC */
128 case 'D': /* D list primary model names */
129 case 'd': /* d dump CRC model */
130 case 'e': /* e echo arguments */
131 case 's': /* s search for algorithm */
132 case 'v': /* v calculate reversed CRC */
133 if(mode) {
134 fprintf(stderr,"%s: more than one mode switch specified. Use %s -h for help.\n", myname, myname);
135 return 0;
136 //exit(EXIT_FAILURE);
137 }
138 mode = c;
139 break;
140 case 'F': /* F force search */
141#ifndef NOFORCE
142 uflags |= C_FORCE;
143#endif
144 break;
145 case 'f': /* f arguments are filenames */
146 uflags |= C_INFILE;
147 break;
148 case 'h': /* h get help / usage */
149 case 'u': /* u get help / usage */
150 case '?': /* ? get help / usage */
151 default:
152 usage();
153 return 0;
154 //exit(EXIT_FAILURE);
155 break;
156 case 'i': /* i: Init value */
157 pptr = &model.init;
158 rflags |= R_HAVEI;
159 goto ippx;
160 case 'k': /* k: polynomial in Koopman notation */
161 pfree(&model.spoly);
162 model.spoly = strtop(optarg, 0, 4);
163 pkchop(&model.spoly);
164 width = plen(model.spoly);
165 rflags |= R_HAVEP;
166 mnovel(&model);
167 break;
168 case 'l': /* l little-endian input and output */
169 model.flags |= P_REFIN;
170 rflags |= R_HAVERI;
171 /* fall through: */
172 case 'L': /* L little-endian output */
173 model.flags |= P_REFOUT;
174 rflags |= R_HAVERO;
175 mnovel(&model);
176 /* fall through: */
177 case 't': /* t left-justified */
178 model.flags &= ~P_RTJUST;
179 break;
180 case 'm': /* m: select preset CRC model */
181 if(!(c = mbynam(&model, optarg))) {
182 fprintf(stderr,"%s: preset model '%s' not found. Use %s -D to list presets.\n", myname, optarg, myname);
183 return 0;
184 //exit(EXIT_FAILURE);
185 }
60e86577 186 if(c < 0){
a71ece51 187 uerror("no preset models available");
60e86577 188 return 0;
189 }
a71ece51 190 /* must set width so that parameter to -ipx is not zeroed */
191 width = plen(model.spoly);
192 rflags |= R_HAVEP | R_HAVEI | R_HAVERI | R_HAVERO | R_HAVEX;
193 break;
194 case 'M': /* M non-augmenting algorithm */
195 model.flags &= ~P_MULXN;
196 break;
197 case 'P': /* P: reversed polynomial */
198 case 'p': /* p: polynomial */
199 pptr = &model.spoly;
200 rflags &= ~R_HAVEQ;
201 rflags |= R_HAVEP;
202ippx:
203 pfree(pptr);
204 *pptr = strtop(optarg, 0, 4);
205 pright(pptr, width);
206 if(c == 'P')
207 prev(pptr);
208 mnovel(&model);
209 break;
210 case 'q': /* q: range end polynomial */
211 pptr = &qpoly;
212 rflags &= ~R_HAVEP;
213 rflags |= R_HAVEQ;
214 goto ippx;
215 case 'S': /* s space between output characters */
216 model.flags |= P_SPACE;
217 break;
218 case 'V': /* v reverse algorithm */
219 /* Distinct from the -v switch as the
220 * user will have to reverse his or her
221 * own arguments. The user cannot dump
222 * the model generated by -v either.
223 */
224 mrev(&model);
225 break;
226 case 'w': /* w: CRC width = order - 1 */
227 width = (unsigned long) atol(optarg);
228 break;
229 case 'X': /* X print uppercase hex */
230 model.flags |= P_UPPER;
231 break;
232 case 'x': /* x: XorOut value */
233 pptr = &model.xorout;
234 rflags |= R_HAVEX;
235 goto ippx;
236 case 'y': /* y little-endian byte order in files */
237 model.flags |= P_LTLBYT;
238 break;
239 case 'z': /* z raw binary arguments */
240 model.flags |= P_DIRECT;
241 break;
242 case -1: /* no more options, continue */
243 ;
244 }
245 } while(c != -1);
246
247 /* canonicalise the model, so the one we dump is the one we
248 * calculate with (not with -s, spoly may be blank which will
249 * normalise to zero and clear init and xorout.)
250 */
251 if(mode != 's')
252 mcanon(&model);
253
254 switch(mode) {
255 case 'v': /* v calculate reversed CRC */
256 /* Distinct from the -V switch as this causes
257 * the arguments and output to be reversed as well.
258 */
259 /* reciprocate Poly */
260 prcp(&model.spoly);
261
262 /* mrev() does:
263 * if(refout) prev(init); else prev(xorout);
264 * but here the entire argument polynomial is
265 * reflected, not just the characters, so RefIn
266 * and RefOut are not inverted as with -V.
267 * Consequently Init is the mirror image of the
268 * one resulting from -V, and so we have:
269 */
270 if(~model.flags & P_REFOUT) {
271 prev(&model.init);
272 prev(&model.xorout);
273 }
274
275 /* swap init and xorout */
276 apoly = model.init;
277 model.init = model.xorout;
278 model.xorout = apoly;
279
280 /* fall through: */
281 case 'c': /* c calculate CRC */
282
283 /* validate inputs */
284 /* if(plen(model.spoly) == 0) {
285 * fprintf(stderr,"%s: no polynomial specified for -%c (add -w WIDTH -p POLY)\n", myname, mode);
286 * exit(EXIT_FAILURE);
287 * }
288 */
289
290 /* in the Williams model, xorout is applied after the refout stage.
291 * as refout is part of ptostr(), we reverse xorout here.
292 */
293 if(model.flags & P_REFOUT)
294 prev(&model.xorout);
295
296 for(; optind < argc; ++optind) {
297 if(uflags & C_INFILE)
298 apoly = rdpoly(argv[optind], model.flags, ibperhx);
299 else
300 apoly = strtop(argv[optind], model.flags, ibperhx);
301
302 if(mode == 'v')
303 prev(&apoly);
304
305 crc = pcrc(apoly, model.spoly, model.init, model.xorout, model.flags);
306
307 if(mode == 'v')
308 prev(&crc);
309
310 string = ptostr(crc, model.flags, obperhx);
311 puts(string);
312 free(string);
313 pfree(&crc);
314 pfree(&apoly);
315 }
316 break;
317 case 'D': /* D dump all models */
318 args = mcount();
60e86577 319 if(!args){
a71ece51 320 uerror("no preset models available");
60e86577 321 return 0;
322 }
a71ece51 323 for(mode = 0; mode < args; ++mode) {
324 mbynum(&model, mode);
325 mcanon(&model);
326 ufound(&model);
327 }
328 break;
329 case 'd': /* d dump CRC model */
330 /* maybe we don't want to do this:
331 * either attaching names to arbitrary models or forcing to a preset
332 * mmatch(&model, M_OVERWR);
333 */
60e86577 334 if(~model.flags & P_MULXN){
a71ece51 335 uerror("not a Williams model compliant algorithm");
60e86577 336 return 0;
337 }
a71ece51 338 string = mtostr(&model);
339 puts(string);
340 free(string);
341 break;
342 case 'e': /* e echo arguments */
343 for(; optind < argc; ++optind) {
344 if(uflags & C_INFILE)
345 apoly = rdpoly(argv[optind], model.flags, ibperhx);
346 else
347 apoly = strtop(argv[optind], model.flags, ibperhx);
348
349 psum(&apoly, model.init, 0UL);
350 string = ptostr(apoly, model.flags, obperhx);
351 puts(string);
352 free(string);
353 pfree(&apoly);
354 }
355 break;
356 case 's': /* s search for algorithm */
60e86577 357 if(!width){
a71ece51 358 uerror("must specify positive -k or -w before -s");
60e86577 359 return 0;
360 }
361 if(~model.flags & P_MULXN){
a71ece51 362 uerror("cannot search for non-Williams compliant models");
60e86577 363 return 0;
364 }
a71ece51 365 praloc(&model.spoly, width);
366 praloc(&model.init, width);
367 praloc(&model.xorout, width);
368 if(!plen(model.spoly))
369 palloc(&model.spoly, width);
370 else
371 width = plen(model.spoly);
372
373 /* special case if qpoly is zero, search to end of range */
374 if(!ptst(qpoly))
375 rflags &= ~R_HAVEQ;
376
377 /* allocate argument array */
378 args = argc - optind;
60e86577 379 if(!(apolys = malloc(args * sizeof(poly_t)))){
a71ece51 380 uerror("cannot allocate memory for argument list");
60e86577 381 return 0;
382 }
a71ece51 383
384 for(pptr = apolys; optind < argc; ++optind) {
385 if(uflags & C_INFILE)
386 *pptr++ = rdpoly(argv[optind], model.flags, ibperhx);
387 else
388 *pptr++ = strtop(argv[optind], model.flags, ibperhx);
389 }
390 /* exit value of pptr is used hereafter! */
391
392 /* if endianness not specified, try
393 * little-endian then big-endian.
394 * NB: crossed-endian algorithms will not be
395 * searched.
396 */
397
398 /* scan against preset models */
399 if(~uflags & C_FORCE) {
400 pass = 0;
401 do {
402 psets = mcount();
403 while(psets) {
404 mbynum(&pset, --psets);
405 /* skip if different width, or refin or refout don't match */
406 if(plen(pset.spoly) != width || (model.flags ^ pset.flags) & (P_REFIN | P_REFOUT))
407 continue;
408 /* skip if the preset doesn't match specified parameters */
409 if(rflags & R_HAVEP && pcmp(&model.spoly, &pset.spoly))
410 continue;
411 if(rflags & R_HAVEI && psncmp(&model.init, &pset.init))
412 continue;
413 if(rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout))
414 continue;
415 apoly = pclone(pset.xorout);
416 if(pset.flags & P_REFOUT)
417 prev(&apoly);
418 for(qptr = apolys; qptr < pptr; ++qptr) {
419 crc = pcrc(*qptr, pset.spoly, pset.init, apoly, 0);
420 if(ptst(crc)) {
421 pfree(&crc);
422 break;
423 } else
424 pfree(&crc);
425 }
426 pfree(&apoly);
427 if(qptr == pptr) {
428 /* the selected model solved all arguments */
429 mcanon(&pset);
430 ufound(&pset);
431 uflags |= C_RESULT;
432 }
433 }
434 mfree(&pset);
435
436 /* toggle refIn/refOut and reflect arguments */
437 if(~rflags & R_HAVERI) {
438 model.flags ^= P_REFIN | P_REFOUT;
439 for(qptr = apolys; qptr < pptr; ++qptr)
440 prevch(qptr, ibperhx);
441 }
442 } while(~rflags & R_HAVERI && ++pass < 2);
443 }
444 if(uflags & C_RESULT) {
445 for(qptr = apolys; qptr < pptr; ++qptr)
446 pfree(qptr);
fe144f12 447 return 1;
a71ece51 448 //exit(EXIT_SUCCESS);
449 }
60e86577 450 if(!(model.flags & P_REFIN) != !(model.flags & P_REFOUT)){
a71ece51 451 uerror("cannot search for crossed-endian models");
60e86577 452 return 0;
453 }
a71ece51 454 pass = 0;
455 do {
456 mptr = candmods = reveng(&model, qpoly, rflags, args, apolys);
457 if(mptr && plen(mptr->spoly))
458 uflags |= C_RESULT;
459 while(mptr && plen(mptr->spoly)) {
460 /* results were printed by the callback
461 * string = mtostr(mptr);
462 * puts(string);
463 * free(string);
464 */
465 mfree(mptr++);
466 }
467 free(candmods);
468 if(~rflags & R_HAVERI) {
469 model.flags ^= P_REFIN | P_REFOUT;
470 for(qptr = apolys; qptr < pptr; ++qptr)
471 prevch(qptr, ibperhx);
472 }
473 } while(~rflags & R_HAVERI && ++pass < 2);
474 for(qptr = apolys; qptr < pptr; ++qptr)
475 pfree(qptr);
476 free(apolys);
477 if(~uflags & C_RESULT)
478 uerror("no models found");
479 break;
480 default: /* no mode specified */
481 fprintf(stderr, "%s: no mode switch specified. Use %s -h for help.\n", myname, myname);
a71ece51 482 //exit(EXIT_FAILURE);
483 }
484
485 return 1;
486 //exit(EXIT_SUCCESS);
487}
488
489void
490ufound(const model_t *model) {
491 /* Callback function to report each model found */
492 char *string;
493
494 if(!model) return;
495 /* generated models will be canonical */
496 string = mtostr(model);
497 puts(string);
498 free(string);
499}
500
501void
502uerror(const char *msg) {
503 /* Callback function to report fatal errors */
504 fprintf(stderr, "%s: %s\n", myname, msg);
505 return;
506 //exit(EXIT_FAILURE);
507}
508
509void
510uprog(const poly_t gpoly, int flags, unsigned long seq) {
511 /* Callback function to report search progress */
512 char *string;
513
514 /* Suppress first report in CLI */
515 if(!seq)
516 return;
517 string = ptostr(gpoly, P_RTJUST, 4);
518 fprintf(stderr, "%s: searching: width=%ld poly=0x%s refin=%s refout=%s\n",
519 myname, plen(gpoly), string,
520 (flags & P_REFIN ? "true" : "false"),
521 (flags & P_REFOUT ? "true" : "false")
522 );
523 free(string);
524}
525
526static poly_t
527rdpoly(const char *name, int flags, int bperhx) {
528 /* read poly from file in chunks and report errors */
529
530 poly_t apoly = PZERO, chunk = PZERO;
531 FILE *input;
532
533 input = oread(name);
534 while(!feof(input) && !ferror(input)) {
535 chunk = filtop(input, BUFFER, flags, bperhx);
536 psum(&apoly, chunk, plen(apoly));
537 pfree(&chunk);
538 }
539 if(ferror(input)) {
540 fprintf(stderr,"%s: error condition on file '%s'\n", myname, name);
541 exit(EXIT_FAILURE);
542 }
543 /* close file unless stdin */
544 if(input == stdin)
545 /* reset EOF condition */
546 clearerr(input);
547 else if(fclose(input)) {
548 fprintf(stderr,"%s: error closing file '%s'\n", myname, name);
549 exit(EXIT_FAILURE);
550 }
551 return(apoly);
552}
553
554static FILE *
555oread(const char *name) {
556 /* open file for reading and report errors */
557 FILE *handle;
558
559 /* recognise special name '-' as standard input */
560 if(*name == '-' && name[1] == '\0')
561 return(stdin);
562 if(!(handle = fopen(name, "rb"))) {
563 fprintf(stderr, "%s: cannot open '%s' for reading\n", myname, name);
564 return 0;
565 //exit(EXIT_FAILURE);
566 }
567 return(handle);
568}
569
570static void
571usage(void) {
572 /* print usage if asked, or if syntax incorrect */
573 fprintf(stderr,
574 "CRC RevEng, an arbitrary-precision CRC calculator and algorithm finder\n"
575 "Usage:\t");
576 fputs(myname, stderr);
577 fprintf(stderr,
578 "\t-cdDesvhu? [-bBfFlLMrStVXyz]\n"
579 "\t\t[-a BITS] [-A OBITS] [-i INIT] [-k KPOLY] [-m MODEL]\n"
580 "\t\t[-p POLY] [-P RPOLY] [-q QPOLY] [-w WIDTH] [-x XOROUT]\n"
581 "\t\t[STRING...]\n"
582 "Options:\n"
583 "\t-a BITS\t\tbits per character (1 to %d)\n"
584 "\t-A OBITS\tbits per output character (1 to %d)\n"
585 "\t-i INIT\t\tinitial register value\n"
586 "\t-k KPOLY\tgenerator in Koopman notation (implies WIDTH)\n"
587 "\t-m MODEL\tpreset CRC algorithm\n"
588 "\t-p POLY\t\tgenerator or search range start polynomial\n"
589 "\t-P RPOLY\treversed generator polynomial\n",
590 BMP_BIT, BMP_BIT);
591 fprintf(stderr,
592 "\t-q QPOLY\tsearch range end polynomial\n"
593 "\t-w WIDTH\tregister size, in bits\n"
594 "\t-x XOROUT\tfinal register XOR value\n"
595 "Modifier switches:\n"
596 "\t-b big-endian CRC\t\t-B big-endian CRC output\n"
597 "\t-f read files named in STRINGs\t-F find presets less quickly\n"
598 "\t-l little-endian CRC\t\t-L little-endian CRC output\n"
599 "\t-M non-augmenting algorithm\t-r right-justified output\n"
600 "\t-S print spaces between chars\t-t left-justified output\n"
601 "\t-V reverse algorithm only\t-X print uppercase hex\n"
602 "\t-y low bytes first in files\t-z raw binary STRINGs\n");
603 fprintf(stderr,
604 "Mode switches:\n"
605 "\t-c calculate CRCs\t\t-d dump algorithm parameters\n"
606 "\t-D list preset algorithms\t-e echo (and reformat) input\n"
607 "\t-s search for algorithm\t\t-v calculate reversed CRCs\n"
02984d68 608 "\t-g search for alg given hex+crc\t-h | -u | -? show this help\n"
609 "Common Use Examples:\n"
610 "\t reveng -g 01020304e3\n"
611 "\t Searches for a known/common crc preset that computes the crc\n"
612 "\t on the end of the given hex string\n"
613 "\t reveng -w 8 -s 01020304e3 010204039d\n"
614 "\t Searches for any possible 8 bit width crc calc that computes\n"
615 "\t the crc on the end of the given hex string(s)\n"
616 "\t reveng -m CRC-8 -c 01020304\n"
617 "\t Calculates the crc-8 of the given hex string\n"
618 "\t reveng -D\n"
619 "\t Outputs a list of all known/common crc models with their\n"
620 "\t preset values\n"
a71ece51 621 "\n"
a78a3d9d 622 "Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016 Gregory Cook\n"
a71ece51 623 "This is free software; see the source for copying conditions. There is NO\n"
624 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
625 "Version "
626 VERSION
627 "\t\t\t\t <http://reveng.sourceforge.net/>\n");
628}
Impressum, Datenschutz