1 //-----------------------------------------------------------------------------
2 // Borrowed initially from https://reveng.sourceforge.io/
3 // Copyright (C) Greg Cook 2019
4 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // See LICENSE.txt for the text of the license.
17 //-----------------------------------------------------------------------------
19 //-----------------------------------------------------------------------------
31 # define STDIN_FILENO 0
32 # endif /* STDIN_FILENO */
42 static int split(char *str
, char *arr
[MAX_ARGS
]) {
47 while (isspace(str
[beginIndex
])) {
50 if (str
[beginIndex
] == '\0') {
53 int endIndex
= beginIndex
;
54 while (str
[endIndex
] && !isspace(str
[endIndex
])) {
57 int len
= endIndex
- beginIndex
;
58 char *tmp
= calloc(len
+ 1, sizeof(char));
59 memcpy(tmp
, &str
[beginIndex
], len
);
61 beginIndex
= endIndex
;
62 if (wordCnt
== MAX_ARGS
)
68 //returns array of model names and the count of models returning
69 // as well as a width array for the width of each model
70 int GetModels(char *Models
[], int *count
, uint8_t *width
) {
72 static model_t model
= MZERO
;
74 poly_t apoly
, crc
, qpoly
= PZERO
, *apolys
= NULL
, *pptr
= NULL
, *qptr
= NULL
;
77 /* stdin must be binary */
79 _setmode(STDIN_FILENO
, _O_BINARY
);
84 if (width
[0] == 0) { //reveng -D
87 PrintAndLogEx(WARNING
, "no preset models available");
90 for (int mode
= 0; mode
< *count
; ++mode
) {
93 size_t size
= (model
.name
&& *model
.name
) ? strlen(model
.name
) : 7;
94 char *tmp
= calloc(size
+ 1, sizeof(char));
96 PrintAndLogEx(WARNING
, "out of memory?");
99 if (model
.name
!= NULL
) {
100 memcpy(tmp
, model
.name
, size
);
102 width
[mode
] = plen(model
.spoly
);
110 int ibperhx
= 8;//, obperhx = 8;
111 int rflags
= 0, uflags
= 0; /* search and UI flags */
112 if (~model
.flags
& P_MULXN
) {
113 PrintAndLogEx(WARNING
, "cannot search for non-Williams compliant models");
116 praloc(&model
.spoly
, (unsigned long)width
[0]);
117 praloc(&model
.init
, (unsigned long)width
[0]);
118 praloc(&model
.xorout
, (unsigned long)width
[0]);
120 if (!plen(model
.spoly
))
121 palloc(&model
.spoly
, (unsigned long)width
[0]);
123 width
[0] = (uint8_t)plen(model
.spoly
);
125 /* special case if qpoly is zero, search to end of range */
131 /* if endianness not specified, try
132 * little-endian then big-endian.
133 * NB: crossed-endian algorithms will not be
136 /* scan against preset models */
137 if (~uflags
& C_NOPCK
) {
141 int psets
= mcount();
144 mbynum(&pset
, --psets
);
146 /* skip if different width, or refin or refout don't match */
147 if (plen(pset
.spoly
) != width
[0] || (model
.flags
^ pset
.flags
) & (P_REFIN
| P_REFOUT
))
149 /* skip if the preset doesn't match specified parameters */
150 if (rflags
& R_HAVEP
&& pcmp(&model
.spoly
, &pset
.spoly
))
152 if (rflags
& R_HAVEI
&& psncmp(&model
.init
, &pset
.init
))
154 if (rflags
& R_HAVEX
&& psncmp(&model
.xorout
, &pset
.xorout
))
157 //for additional args (not used yet, maybe future?)
158 apoly
= pclone(pset
.xorout
);
160 if (pset
.flags
& P_REFOUT
)
164 for (qptr
= apolys
; qptr
< pptr
; ++qptr
) {
165 crc
= pcrc(*qptr
, pset
.spoly
, pset
.init
, apoly
, 0);
176 /* the selected model solved all arguments */
179 size_t size
= (pset
.name
&& *pset
.name
) ? strlen(pset
.name
) : 7;
180 //PrintAndLogEx(NORMAL, "Size: %d, %s, count: %d",size,pset.name, Cnt);
181 char *tmp
= calloc(size
+ 1, sizeof(char));
183 PrintAndLogEx(WARNING
, "out of memory?");
186 width
[Cnt
] = width
[0];
187 memcpy(tmp
, pset
.name
, size
);
195 /* toggle refIn/refOut and reflect arguments */
196 if (~rflags
& R_HAVERI
) {
197 model
.flags
^= P_REFIN
| P_REFOUT
;
198 for (qptr
= apolys
; qptr
< pptr
; ++qptr
) {
199 prevch(qptr
, ibperhx
);
202 } while (~rflags
& R_HAVERI
&& ++pass
< 2);
204 //got everything now free the memory...
206 if (uflags
& C_RESULT
) {
207 for (qptr
= apolys
; qptr
< pptr
; ++qptr
) {
211 if (uflags
& C_NOBFS
&& ~rflags
& R_HAVEP
) {
212 PrintAndLogEx(WARNING
, "no models found");
216 if (!(model
.flags
& P_REFIN
) != !(model
.flags
& P_REFOUT
)) {
217 PrintAndLogEx(WARNING
, "cannot search for crossed-endian models");
223 model_t
*candmods
= reveng(&model
, qpoly
, rflags
, args
, apolys
);
224 model_t
*mptr
= candmods
;
225 if (mptr
&& plen(mptr
->spoly
)) {
228 while (mptr
&& plen(mptr
->spoly
)) {
232 if (~rflags
& R_HAVERI
) {
233 model
.flags
^= P_REFIN
| P_REFOUT
;
234 for (qptr
= apolys
; qptr
< pptr
; ++qptr
) {
235 prevch(qptr
, ibperhx
);
238 } while (~rflags
& R_HAVERI
&& ++pass
< 2);
240 for (qptr
= apolys
; qptr
< pptr
; ++qptr
) {
246 if (~uflags
& C_RESULT
) {
247 PrintAndLogEx(WARNING
, "no models found");
255 //inModel = valid model name string - CRC-8
256 //inHexStr = input hex string to calculate crc on
257 //reverse = reverse calc option if true
258 //endian = {0 = calc default endian input and output, b = big endian input and output, B = big endian output, r = right justified
259 // l = little endian input and output, L = little endian output only, t = left justified}
260 //result = calculated crc hex string
261 int RunModel(char *inModel
, char *inHexStr
, bool reverse
, char endian
, char *result
) {
263 static model_t model
= MZERO
;
265 int ibperhx
= 8, obperhx
= 8;
266 // int rflags = 0; // search flags
271 // stdin must be binary
273 _setmode(STDIN_FILENO
, _O_BINARY
);
278 int c
= mbynam(&model
, inModel
);
280 PrintAndLogEx(ERR
, "error: preset model '%s' not found. Use reveng -D to list presets. [%d]", inModel
, c
);
284 PrintAndLogEx(WARNING
, "no preset models available");
287 // rflags |= R_HAVEP | R_HAVEI | R_HAVERI | R_HAVERO | R_HAVEX;
291 case 'b': /* b big-endian (RefIn = false, RefOut = false ) */
292 model
.flags
&= ~P_REFIN
;
293 //rflags |= R_HAVERI;
295 case 'B': /* B big-endian output (RefOut = false) */
296 model
.flags
&= ~P_REFOUT
;
297 //rflags |= R_HAVERO;
300 case 'r': /* r right-justified */
301 model
.flags
|= P_RTJUST
;
303 case 'l': /* l little-endian input and output */
304 model
.flags
|= P_REFIN
;
305 //rflags |= R_HAVERI;
307 case 'L': /* L little-endian output */
308 model
.flags
|= P_REFOUT
;
309 //rflags |= R_HAVERO;
312 case 't': /* t left-justified */
313 model
.flags
&= ~P_RTJUST
;
316 /* canonicalise the model, so the one we dump is the one we
317 * calculate with (not with -s, spoly may be blank which will
318 * normalise to zero and clear init and xorout.)
324 // v calculate reversed CRC
325 /* Distinct from the -V switch as this causes
326 * the arguments and output to be reversed as well.
332 * if(refout) prev(init); else prev(xorout);
333 * but here the entire argument polynomial is
334 * reflected, not just the characters, so RefIn
335 * and RefOut are not inverted as with -V.
336 * Consequently Init is the mirror image of the
337 * one resulting from -V, and so we have:
339 if (~model
.flags
& P_REFOUT
) {
344 // swap init and xorout
346 model
.init
= model
.xorout
;
347 model
.xorout
= apoly
;
351 /* in the Williams model, xorout is applied after the refout stage.
352 * as refout is part of ptostr(), we reverse xorout here.
354 if (model
.flags
& P_REFOUT
)
357 apoly
= strtop(inHexStr
, model
.flags
, ibperhx
);
362 crc
= pcrc(apoly
, model
.spoly
, model
.init
, model
.xorout
, model
.flags
);
367 string
= ptostr(crc
, model
.flags
, obperhx
);
368 for (int i
= 0; i
< 50; i
++) {
369 result
[i
] = string
[i
];
370 if (result
[i
] == 0) break;
378 //test call to RunModel
379 static int CmdrevengTestC(const char *Cmd) {
381 char inModel[30] = {0x00};
382 char inHexStr[30] = {0x00};
386 dataLen = param_getstr(Cmd, cmdp++, inModel, sizeof(inModel));
387 if (dataLen < 4) return 0;
388 dataLen = param_getstr(Cmd, cmdp++, inHexStr, sizeof(inHexStr));
389 if (dataLen < 4) return 0;
390 bool reverse = (param_get8(Cmd, cmdp++)) ? true : false;
391 endian = param_getchar(Cmd, cmdp++);
393 //PrintAndLogEx(NORMAL, "mod: %s, hex: %s, rev %d", inModel, inHexStr, reverse);
394 int ans = RunModel(inModel, inHexStr, reverse, endian, result);
397 PrintAndLogEx(SUCCESS, "result: %s", result);
401 //returns a calloced string (needs to be freed)
402 static char *SwapEndianStr(const char *inStr
, const size_t len
, const uint8_t blockSize
) {
403 char *tmp
= calloc(len
+ 1, sizeof(char));
404 for (uint8_t block
= 0; block
< (uint8_t)(len
/ blockSize
); block
++) {
405 for (size_t i
= 0; i
< blockSize
; i
+= 2) {
406 tmp
[i
+ (blockSize
* block
)] = inStr
[(blockSize
- 1 - i
- 1) + (blockSize
* block
)];
407 tmp
[i
+ (blockSize
* block
) + 1] = inStr
[(blockSize
- 1 - i
) + (blockSize
* block
)];
413 // takes hex string in and searches for a matching result (hex string must include checksum)
414 static int CmdrevengSearch(const char *Cmd
) {
418 char inHexStr
[256] = {0x00};
419 int dataLen
= param_getstr(Cmd
, 0, inHexStr
, sizeof(inHexStr
));
420 if (dataLen
< 4) return 0;
422 // these two arrays, must match preset size.
423 char *Models
[NMODELS
];
424 uint8_t width
[NMODELS
] = {0};
427 char result
[50 + 1] = {0};
428 char revResult
[50 + 1] = {0};
429 int ans
= GetModels(Models
, &count
, width
);
432 for (int i
= 0; i
< count
; i
++) {
440 // try each model and get result
441 for (int i
= 0; i
< count
; i
++) {
446 // round up to # of characters in this model's crc
447 uint8_t crcChars
= ((width
[i
] + 7) / 8) * 2;
448 // can't test a model that has more crc digits than our data
449 if (crcChars
>= dataLen
) {
455 , "DEBUG: dataLen %d, crcChars %u, width[i] %u"
466 memset(result
, 0, sizeof(result
));
467 char *inCRC
= calloc(crcChars
+ 1, sizeof(char));
472 memcpy(inCRC
, inHexStr
+ (dataLen
- crcChars
), crcChars
);
474 char *outHex
= calloc(dataLen
- crcChars
+ 1, sizeof(char));
475 if (outHex
== NULL
) {
480 memcpy(outHex
, inHexStr
, dataLen
- crcChars
);
482 ans
= RunModel(Models
[i
], outHex
, false, 0, result
);
488 if (memcmp(result
, inCRC
, crcChars
) == 0) {
489 PrintAndLogEx(SUCCESS
, "model... " _YELLOW_("%s"), Models
[i
]);
490 PrintAndLogEx(SUCCESS
, "value... %s\n", result
);
491 //optional - stop searching if found...
495 char *swapEndian
= SwapEndianStr(result
, crcChars
, crcChars
);
496 if (memcmp(swapEndian
, inCRC
, crcChars
) == 0) {
497 PrintAndLogEx(SUCCESS
, "model... " _YELLOW_("%s"), Models
[i
]);
498 PrintAndLogEx(SUCCESS
, "value endian swapped... %s\n", swapEndian
);
499 // optional - stop searching if found...
506 ans
= RunModel(Models
[i
], outHex
, true, 0, revResult
);
508 str_lower(revResult
);
511 if (memcmp(revResult
, inCRC
, crcChars
) == 0) {
512 PrintAndLogEx(SUCCESS
, "model reversed... " _YELLOW_("%s"), Models
[i
]);
513 PrintAndLogEx(SUCCESS
, "value... %s\n", revResult
);
514 // optional - stop searching if found...
518 char *swapEndian
= SwapEndianStr(revResult
, crcChars
, crcChars
);
519 if (memcmp(swapEndian
, inCRC
, crcChars
) == 0) {
520 PrintAndLogEx(SUCCESS
, "model reversed... " _YELLOW_("%s"), Models
[i
]);
521 PrintAndLogEx(SUCCESS
, "value endian swapped... %s\n", swapEndian
);
522 // optional - stop searching if found...
534 if (found
== false) {
535 PrintAndLogEx(FAILED
, "\nno matches found\n");
540 int CmdCrc(const char *Cmd
) {
542 snprintf(c
, sizeof(c
), "reveng ");
543 snprintf(c
+ strlen(c
), sizeof(c
) - strlen(c
), Cmd
, strlen(Cmd
));
545 char *argv
[MAX_ARGS
];
546 int argc
= split(c
, argv
);
548 if (argc
== 3 && memcmp(argv
[1], "-g", 2) == 0) {
549 CmdrevengSearch(argv
[2]);
551 reveng_main(argc
, argv
);
554 for (int i
= 0; i
< argc
; ++i
) {