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