├── bittypes.h ├── Makefile ├── UNLICENSE ├── bigint.c ├── forge.h ├── crc.h ├── forge.c ├── bigint.h ├── README.md ├── check.sh ├── crc.c └── crchack.c /bittypes.h: -------------------------------------------------------------------------------- 1 | #ifndef BITTYPES_H 2 | #define BITTYPES_H 3 | 4 | #include 5 | 6 | /* Bit size and bit offset types */ 7 | typedef uintmax_t bitsize_t; 8 | typedef intmax_t bitoffset_t; 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -g 2 | CFLAGS += -Wall -std=c99 -pedantic 3 | LDLIBS ?= 4 | 5 | all: crchack 6 | 7 | crchack: crchack.o bigint.o crc.o forge.o 8 | $(CC) $(CFLAGS) $^ -o $@ $(LDLIBS) 9 | 10 | check: crchack 11 | ./check.sh 12 | 13 | clean: 14 | $(RM) crchack *.o 15 | 16 | .PHONY: all check clean 17 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /bigint.c: -------------------------------------------------------------------------------- 1 | #include "bigint.h" 2 | 3 | void bigint_fprint(FILE *stream, const struct bigint *dest) 4 | { 5 | size_t i, j, k = LIMB_BITS/4 - ((dest->bits + 3) % LIMB_BITS)/4; 6 | for (i = 1, j = bigint_limbs(dest); i <= j; i++) { 7 | const limb_t limb = dest->limb[j-i]; 8 | for (k %= LIMB_BITS/4; k < LIMB_BITS/4; k++) { 9 | unsigned char nibble = (limb >> (LIMB_BITS - 4*(k+1))) & 0x0F; 10 | fputc("0123456789abcdef"[nibble], stream); 11 | } 12 | } 13 | } 14 | 15 | struct bigint *bigint_from_string(struct bigint *dest, const char *hex) 16 | { 17 | int neg; 18 | size_t len, bits, i; 19 | 20 | /* Validate input string */ 21 | if ((neg = (hex[0] == '-'))) 22 | hex++; 23 | if (hex[0] == '0' && hex[1] == 'x') 24 | hex += 2; 25 | len = strspn(hex, "0123456789abcdefABCDEF"); 26 | if (!len || hex[len] != '\0') 27 | return NULL; 28 | 29 | /* Determine length in bits */ 30 | for (i = 0; i < len-1 && hex[i] == '0'; i++); 31 | bits = (len-i)*4; 32 | bits -= (hex[i] < '8') + (hex[i] < '4') /* trollface.jpg */ 33 | + (hex[i] < '2') + (hex[i] < '1'); 34 | if (bits > dest->bits) 35 | return NULL; /* need moar space */ 36 | 37 | /* Parse into limbs */ 38 | bigint_load_zeros(dest); 39 | for (i = 0; i < bits; i += 4) { 40 | char v, c = hex[len-1 - i/4]; 41 | if (c >= '0' && c <= '9') { 42 | v = c - '0'; 43 | } else if (c >= 'A' && c <= 'F') { 44 | v = 10 + (c - 'A'); 45 | } else /*if (c >= 'a' && c <= 'f')*/ { 46 | v = 10 + (c - 'a'); 47 | } 48 | dest->limb[i/LIMB_BITS] |= (limb_t)v << (i % LIMB_BITS); 49 | } 50 | 51 | /* Handle negative values */ 52 | if (neg) { 53 | limb_t carry = 1; 54 | size_t j = bigint_limbs(dest); 55 | for (i = 0; i < j; i++) { 56 | dest->limb[i] = ~dest->limb[i] + carry; 57 | carry &= dest->limb[i] == 0; 58 | } 59 | } 60 | 61 | return dest; 62 | } 63 | -------------------------------------------------------------------------------- /forge.h: -------------------------------------------------------------------------------- 1 | #ifndef FORGE_H 2 | #define FORGE_H 3 | 4 | #include "bigint.h" 5 | 6 | /* 7 | * Forge a linear checksum by mutating specified bits of an input message. 8 | * 9 | * Parameter `target_checksum` defines the desired target checksum. 10 | * 11 | * `H(pos, out)` is a caller-defined hash function that computes a checksum of 12 | * an input message with a single bit flipped at the position `pos` (the output 13 | * buffer `out` receives the resulting checksum value). Additionally, if `pos` 14 | * is an invalid position exceeding the input message length, then the function 15 | * must return the checksum of the unmodified input message. Yes, the interface 16 | * is confusing as hell, but this is necessary for optimization purposes. 17 | * 18 | * The checksum function `H(msg)` should satisfy a "weak" linearity property: 19 | * 20 | * +-----------------------------------------------------+ 21 | * | H(x ^ y ^ z) = H(x) ^ H(y) ^ H(z) for |x|=|y|=|z|. | 22 | * +-----------------------------------------------------+ 23 | * 24 | * For example CRC(x ^ y ^ z) = CRC(x) ^ CRC(y) ^ CRC(z) holds for all commonly 25 | * used and standardized CRC functions. Therefore, CRC checksums are supported. 26 | * 27 | * Array `bits[]` (containing `nbits` elements) specifies the indices of 28 | * mutable bits in the input message. Bytes start at bit indices 0, 8, 16... 29 | * and bits within a byte are numbered from the LSB to the MSB (e.g., index 10 30 | * corresponds to the third least significant bit of the second byte). 31 | * 32 | * A successful `forge()` function call returns a non-negative value `n >= 0` 33 | * and permutates `bits[]` array so that the first `n` elements contain indices 34 | * of bit flips necessary for producing the desired checksum. 35 | * 36 | * On error, `forge()` returns a negative value whose absolute value represents 37 | * the approximate number of extra mutable bits required for the `bits[]` array 38 | * to achieve the target checksum and make the forging operation successful. 39 | */ 40 | 41 | bitoffset_t forge(const struct bigint *target_checksum, 42 | void (*H)(bitsize_t pos, struct bigint *out), 43 | bitsize_t bits[], size_t nbits); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /crc.h: -------------------------------------------------------------------------------- 1 | #ifndef CRC_H 2 | #define CRC_H 3 | 4 | #include "bigint.h" 5 | 6 | /* CRC algorithm parameters */ 7 | struct crc_config { 8 | unsigned int width; /* CRC register width in bits */ 9 | struct bigint poly; /* generator polynomial */ 10 | struct bigint init; /* initial register value */ 11 | struct bigint xor_out; /* final register XOR mask */ 12 | int reflect_in; /* reverse input bits (LSB first instead of MSB) */ 13 | int reflect_out; /* reverse final register */ 14 | }; 15 | 16 | /* Calculate CRC checksum of a (j-i)-bit message msg[i..j-1] */ 17 | void crc_bits(const struct crc_config *crc, 18 | const void *msg, bitsize_t i, bitsize_t j, 19 | struct bigint *checksum); 20 | 21 | /* Calculate CRC checksum of a len-byte message */ 22 | void crc(const struct crc_config *crc, const void *msg, size_t len, 23 | struct bigint *checksum); 24 | 25 | /* Append a (j-i)-bit message msg[i..j-1] to an existing checksum */ 26 | void crc_append_bits(const struct crc_config *crc, 27 | const void *msg, bitsize_t i, bitsize_t j, 28 | struct bigint *checksum); 29 | 30 | /* Append a len-byte message to an existing checksum */ 31 | void crc_append(const struct crc_config *crc, const void *msg, size_t len, 32 | struct bigint *checksum); 33 | 34 | /* CRC sparse engine for efficient checksum calculation of sparse inputs */ 35 | struct crc_sparse { 36 | struct crc_config crc; /* CRC algorithm */ 37 | bitsize_t size; /* message size in bits */ 38 | 39 | struct bigint *D; /* difference matrix */ 40 | struct bigint *L; /* left matrix table */ 41 | struct bigint *R; /* right matrix table */ 42 | struct bigint *PQ; /* P & Q work matrix */ 43 | }; 44 | 45 | /* New CRC sparse engine for size-bit long message */ 46 | struct crc_sparse *crc_sparse_new(const struct crc_config *crc, bitsize_t size); 47 | 48 | /* Adjust CRC checksum for a message with bit flip in the given position */ 49 | int crc_sparse_1bit(struct crc_sparse *engine, bitsize_t bitpos, 50 | struct bigint *checksum); 51 | 52 | /* Delete CRC sparse engine */ 53 | void crc_sparse_delete(struct crc_sparse *engine); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /forge.c: -------------------------------------------------------------------------------- 1 | #include "forge.h" 2 | 3 | bitoffset_t forge(const struct bigint *target_checksum, 4 | void (*H)(bitsize_t pos, struct bigint *out), 5 | bitsize_t bits[], size_t nbits) 6 | { 7 | bitoffset_t ret; 8 | bitsize_t i, j, p; 9 | struct bigint acc, *AT; 10 | const bitsize_t width = target_checksum->bits; 11 | 12 | /* Initialize accumulator vector */ 13 | if (!bigint_init(&acc, width)) 14 | return -(bitoffset_t)(width + 1); 15 | 16 | /* Initialize bigints for matrix A */ 17 | if (!(AT = calloc(nbits, sizeof(struct bigint)))) { 18 | bigint_destroy(&acc); 19 | return -(bitoffset_t)(width + 2); 20 | } 21 | for (i = 0; i < nbits; i++) { 22 | if (!bigint_init(&AT[i], width)) { 23 | ret = -(bitoffset_t)(width + 2); 24 | nbits = i; 25 | goto finish; 26 | } 27 | } 28 | 29 | /* A[i] = H(msg ^ bits[i]) ^ H(msg) */ 30 | H(~(bitsize_t)0, &acc); 31 | for (i = 0; i < nbits; i++) { 32 | H(bits[i], &AT[i]); 33 | bigint_xor(&AT[i], &acc); 34 | } 35 | 36 | /* 37 | * Solve Ax = b where b = target_checksum ^ H(msg). 38 | * 39 | * Accumulator combines the vectors: x = acc[..i] and b = acc[i..]. 40 | */ 41 | p = 0; 42 | bigint_xor(&acc, target_checksum); 43 | for (i = 0; i < width; i++) { 44 | /* Find a pivot row with a non-zero column i */ 45 | for (j = p; j < nbits; j++) { 46 | if (bigint_get_bit(&AT[j], i)) { 47 | /* Swap rows p and j so that row p becomes the pivot row */ 48 | bitsize_t tmp = bits[j]; 49 | bits[j] = bits[p]; 50 | bits[p] = tmp; 51 | bigint_swap(&AT[j], &AT[p]); 52 | break; 53 | } 54 | } 55 | 56 | if (j < nbits) { 57 | /* Pivot found */ 58 | /* Zero out column i in rows below the pivot */ 59 | for (j = p+1; j < nbits; j++) { 60 | if (bigint_get_bit(&AT[j], i)) { 61 | bigint_xor(&AT[j], &AT[p]); 62 | bigint_flip_bit(&AT[j], p); 63 | } 64 | } 65 | 66 | if (bigint_get_bit(&acc, i)) { 67 | bigint_xor(&acc, &AT[p]); 68 | bigint_set_bit(&acc, p); 69 | } 70 | 71 | p++; 72 | } else if (bigint_get_bit(&acc, i)) { 73 | /* Pivot required but zero column found. Need more bits! */ 74 | ret = -(bitoffset_t)(width - i); 75 | goto finish; 76 | } 77 | } 78 | 79 | /* Move bit flips to the beginning of the bits array */ 80 | ret = 0; 81 | for (i = 0; i < width; i++) { 82 | if (bigint_get_bit(&acc, i)) { 83 | bitsize_t tmp = bits[i]; 84 | bits[i] = bits[ret]; 85 | bits[ret] = tmp; 86 | ret++; 87 | } 88 | } 89 | 90 | finish: 91 | bigint_destroy(&acc); 92 | for (i = 0; i < nbits; i++) 93 | bigint_destroy(&AT[i]); 94 | free(AT); 95 | return ret; 96 | } 97 | -------------------------------------------------------------------------------- /bigint.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Rudimentary big integers for crchack. 3 | */ 4 | #ifndef BIGINT_H 5 | #define BIGINT_H 6 | 7 | #include "bittypes.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | typedef unsigned long limb_t; 14 | 15 | struct bigint { 16 | limb_t *limb; 17 | bitsize_t bits; 18 | }; 19 | 20 | #define LIMB_BITS (8 * (bitsize_t)sizeof(limb_t)) 21 | #define BITS_TO_LIMBS(x) ((size_t)(((x) + LIMB_BITS-1) / LIMB_BITS)) 22 | 23 | /* Size of bigint in bits */ 24 | static inline bitsize_t bigint_bits(const struct bigint *dest) 25 | { 26 | return dest->bits; 27 | } 28 | 29 | /* Number of bigint limbs */ 30 | static inline size_t bigint_limbs(const struct bigint *dest) 31 | { 32 | return BITS_TO_LIMBS(bigint_bits(dest)); 33 | } 34 | 35 | /* Initialize bigint structure */ 36 | static inline struct bigint *bigint_init(struct bigint *dest, bitsize_t bits) 37 | { 38 | if (bits && (dest->limb = calloc(BITS_TO_LIMBS(bits), sizeof(limb_t)))) { 39 | dest->bits = bits; 40 | return dest; 41 | } 42 | dest->limb = NULL; 43 | dest->bits = 0; 44 | return NULL; 45 | } 46 | 47 | /* Destroy bigint */ 48 | static inline void bigint_destroy(struct bigint *dest) 49 | { 50 | free(dest->limb); 51 | dest->limb = NULL; 52 | dest->bits = 0; 53 | } 54 | 55 | /* Allocate and initialize array of n bigint structures */ 56 | static inline struct bigint *bigint_array_new(size_t n, bitsize_t bits) 57 | { 58 | struct bigint *arr; 59 | size_t limbs = BITS_TO_LIMBS(bits); 60 | if ((arr = malloc(n * sizeof(struct bigint) + n * limbs*sizeof(limb_t)))) { 61 | size_t i; 62 | limb_t *limb = (limb_t *)&arr[n]; 63 | memset(limb, 0, n * sizeof(limb_t)); 64 | for (i = 0; i < n; i++) { 65 | arr[i].bits = bits; 66 | arr[i].limb = limb; 67 | limb += limbs; 68 | } 69 | } 70 | return arr; 71 | } 72 | 73 | /* Delete n-length bigint array */ 74 | static inline void bigint_array_delete(struct bigint *arr) 75 | { 76 | free(arr); 77 | } 78 | 79 | /* Print hexadecimal representation of a bigint to stream */ 80 | void bigint_fprint(FILE *stream, const struct bigint *dest); 81 | #define bigint_print(x) (bigint_fprint(stdout, (x))) 82 | 83 | /* Set all bits to zero */ 84 | static inline void bigint_load_zeros(struct bigint *dest) 85 | { 86 | memset(dest->limb, 0, bigint_limbs(dest) * sizeof(limb_t)); 87 | } 88 | 89 | /* Set all bits to one */ 90 | static inline void bigint_load_ones(struct bigint *dest) 91 | { 92 | memset(dest->limb, -1, bigint_limbs(dest) * sizeof(limb_t)); 93 | } 94 | 95 | /* Test for zero value */ 96 | static inline int bigint_is_zero(const struct bigint *dest) 97 | { 98 | size_t i, j = bigint_limbs(dest); 99 | for (i = 0; i < j && !dest->limb[i]; i++); 100 | return i == j; 101 | } 102 | 103 | /* 104 | * Load bigint from a hex string. 105 | * 106 | * Fails if the input string is an invalid hexadecimal number, or if destination 107 | * bigint is too small to hold the value. Returns dest (or NULL on failure). 108 | */ 109 | struct bigint *bigint_from_string(struct bigint *dest, const char *hex); 110 | 111 | /* Get/set the nth least significant bit (n = 0, 1, 2, ..., dest->bits - 1) */ 112 | static inline int bigint_get_bit(const struct bigint *dest, bitsize_t n) 113 | { 114 | return (dest->limb[n / LIMB_BITS] >> (n % LIMB_BITS)) & 1; 115 | } 116 | #define bigint_lsb(x) ((x)->limb[0] & 1) 117 | #define bigint_msb(x) (bigint_get_bit((x), (x)->bits-1)) 118 | 119 | static inline void bigint_set_bit(struct bigint *dest, bitsize_t n) 120 | { 121 | dest->limb[n / LIMB_BITS] |= ((limb_t)1 << (n % LIMB_BITS)); 122 | } 123 | #define bigint_set_lsb(x) ((x)->limb[0] |= 1) 124 | #define bigint_set_msb(x) (bigint_set_bit((x), (x)->bits-1)) 125 | 126 | static inline void bigint_clear_bit(struct bigint *dest, bitsize_t n) 127 | { 128 | dest->limb[n / LIMB_BITS] &= ~((limb_t)1 << (n % LIMB_BITS)); 129 | } 130 | #define bigint_clear_lsb(x) ((x)->limb[0] &= ~(limb_t)1) 131 | #define bigint_clear_msb(x) (bigint_clear_bit((x), (x)->bits-1)) 132 | 133 | static inline void bigint_flip_bit(struct bigint *dest, bitsize_t n) 134 | { 135 | dest->limb[n / LIMB_BITS] ^= ((limb_t)1 << (n % LIMB_BITS)); 136 | } 137 | #define bigint_flip_lsb(x) ((x)->limb[0] ^= 1) 138 | #define bigint_flip_msb(x) (bigint_flip_bit((x), (x)->bits-1)) 139 | 140 | /* Move (copy) source bigint to destination */ 141 | static inline struct bigint *bigint_mov(struct bigint *dest, 142 | const struct bigint *src) 143 | { 144 | memcpy(dest->limb, src->limb, bigint_limbs(dest) * sizeof(limb_t)); 145 | return dest; 146 | } 147 | 148 | /* Bitwise NOT */ 149 | static inline struct bigint *bigint_not(struct bigint *dest) 150 | { 151 | size_t i, j = bigint_limbs(dest); 152 | for (i = 0; i < j; i++) dest->limb[i] = ~dest->limb[i]; 153 | return dest; 154 | } 155 | 156 | /* Bitwise XOR */ 157 | static inline struct bigint *bigint_xor(struct bigint *dest, 158 | const struct bigint *src) 159 | { 160 | size_t i, j = bigint_limbs(dest); 161 | for (i = 0; i < j; i++) dest->limb[i] ^= src->limb[i]; 162 | return dest; 163 | } 164 | 165 | /* Bitwise AND */ 166 | static inline struct bigint *bigint_and(struct bigint *dest, 167 | const struct bigint *src) 168 | { 169 | size_t i, j = bigint_limbs(dest); 170 | for (i = 0; i < j; i++) dest->limb[i] &= src->limb[i]; 171 | return dest; 172 | } 173 | 174 | /* Swap the values of two bigints */ 175 | static inline void bigint_swap(struct bigint *a, struct bigint *b) { 176 | limb_t *limb = a->limb; 177 | a->limb = b->limb; 178 | b->limb = limb; 179 | } 180 | 181 | /* Bit-shift to the left by 1 */ 182 | static inline struct bigint *bigint_shl_1(struct bigint *dest) 183 | { 184 | size_t i, j; 185 | limb_t carry = 0; 186 | for (i = 0, j = bigint_limbs(dest) - 1; i < j; i++) { 187 | const limb_t c = (dest->limb[i] >> (LIMB_BITS - 1)) & 1; 188 | dest->limb[i] = (dest->limb[i] << 1) | carry; 189 | carry = c; 190 | } 191 | dest->limb[i] &= ((limb_t)1 << ((dest->bits - 1) % LIMB_BITS)) - 1; 192 | dest->limb[i] = (dest->limb[i] << 1) | carry; 193 | return dest; 194 | } 195 | 196 | /* Bit-shift to the right by 1 */ 197 | static inline struct bigint *bigint_shr_1(struct bigint *dest) 198 | { 199 | size_t i, j; 200 | for (i = 0, j = bigint_limbs(dest) - 1; i < j; i++) { 201 | const limb_t c = dest->limb[i+1] << (LIMB_BITS-1); 202 | dest->limb[i] = (dest->limb[i] >> 1) | c; 203 | } 204 | dest->limb[i] >>= 1; 205 | return dest; 206 | } 207 | 208 | /* Reverse the bits in a bigint (LSB becomes MSB and vice versa and so on) */ 209 | static inline struct bigint *bigint_reflect(struct bigint *dest) 210 | { 211 | struct bigint acc; 212 | if (bigint_init(&acc, dest->bits)) { 213 | bitsize_t i; 214 | bigint_mov(&acc, dest); 215 | bigint_load_zeros(dest); 216 | for (i = 0; i < dest->bits; i++) { 217 | bigint_shl_1(dest); 218 | if (bigint_lsb(&acc)) 219 | bigint_set_lsb(dest); 220 | bigint_shr_1(&acc); 221 | } 222 | bigint_destroy(&acc); 223 | } 224 | return dest; 225 | } 226 | 227 | #endif 228 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **crchack** is a public domain tool to force CRC checksums of input messages to 2 | arbitrarily chosen values. The main advantage over existing CRC alteration 3 | tools is the ability to obtain the target checksum by changing non-contiguous 4 | input bits. Furthermore, crchack supports *all* CRC algorithms including custom 5 | CRC parameters. crchack is also an arbitrary-precision CRC calculator. 6 | 7 | - [Usage](#usage) 8 | - [Examples](#examples) 9 | - [CRC algorithms](#crc-algorithms) 10 | - [How it works?](#how-it-works) 11 | - [Use cases](#use-cases) 12 | 13 | 14 | # Usage 15 | 16 | ``` 17 | usage: ./crchack [options] file [target_checksum] 18 | 19 | options: 20 | -o pos byte.bit position of mutable input bits 21 | -O pos position offset from the end of the input 22 | -b l:r:s specify bits at positions l..r with step s 23 | -h show this help 24 | -v verbose mode 25 | 26 | CRC parameters (default: CRC-32): 27 | -p poly generator polynomial -w size register size in bits 28 | -i init initial register value -x xor final register XOR mask 29 | -r reverse input bytes -R reverse final register 30 | ``` 31 | 32 | Input message is read from *file* and the patched message is written to stdout. 33 | By default, crchack appends 4 bytes to the input producing a new message with 34 | the target CRC-32 checksum. Other CRC algorithms are defined using the CRC 35 | parameters. If *target_checksum* is not given, then crchack calculates the CRC 36 | checksum of the input message and writes the result to stdout. 37 | 38 | 39 | # Examples 40 | 41 | ``` 42 | [crchack]$ echo "hello" > foo 43 | [crchack]$ ./crchack foo 44 | 363a3020 45 | [crchack]$ ./crchack foo 42424242 > bar 46 | [crchack]$ ./crchack bar 47 | 42424242 48 | [crchack]$ xxd bar 49 | 00000000: 6865 6c6c 6f0a d2d1 eb7a hello....z 50 | 51 | [crchack]$ echo "foobar" | ./crchack - DEADBEEF | ./crchack - 52 | deadbeef 53 | 54 | [crchack]$ echo "PING 1234" | ./crchack - 55 | 29092540 56 | [crchack]$ echo "PING XXXX" | ./crchack -o5 - 29092540 57 | PING 1234 58 | ``` 59 | 60 | In order to modify non-consecutive input message bits, specify the mutable bits 61 | with `-b start:end:step` switches using Python-style *slices* which represent 62 | the bits between positions `start` (inclusive) and `end` (exclusive) with 63 | successive bits `step` bits apart. If empty, `start` is the beginning of the 64 | message, `end` is the end of the message, and `step` equals 1 bit selecting all 65 | bits in between. For example, `-b 4:` selects all bits starting from the *byte* 66 | position 4 (note that `-b 4` without the colon selects only the 32nd bit). 67 | 68 | ``` 69 | [crchack]$ echo "aXbXXcXd" | ./crchack -b1:2 -b3:5 -b6:7 - cafebabe | xxd 70 | 00000000: 61d6 6298 f763 4d64 0a a.b..cMd. 71 | [crchack]$ echo -e "a\xD6b\x98\xF7c\x4Dd" | ./crchack - 72 | cafebabe 73 | 74 | [crchack]$ echo "1234PQPQ" | ./crchack -b 4: - 12345678 75 | 1234u>|7 76 | [crchack]$ echo "1234PQPQ" | ./crchack -b :4 - 12345678 77 | _MLPPQPQ 78 | [crchack]$ echo "1234u>|7" | ./crchack - && echo "_MLPPQPQ" | ./crchack - 79 | 12345678 80 | 12345678 81 | ``` 82 | 83 | The byte position is optionally followed by a dot-separated *bit* position. For 84 | instance, `-b0.32`, `-b2.16` and `-b4.0` all select the same 32nd bit. Within a 85 | byte, bits are numbered from the least significant bit (0) to the most 86 | significant bit (7). Negative positions are treated as offsets from the end of 87 | the input. Built-in expression parser supports `0x`-prefixed hexadecimal 88 | numbers as well as basic arithmetic operations `+-*/`. Finally, `end` can be 89 | defined relative to `start` by starting the position expression with unary `+` 90 | operator. 91 | 92 | ``` 93 | [crchack]$ python -c 'print("A"*32)' | ./crchack -b "0.5:+.8*32:.8" - 1337c0de 94 | AAAaAaaaaaAAAaAaAaAaAaaAaAaaAAaA 95 | [crchack]$ echo "AAAaAaaaaaAAAaAaAaAaAaaAaAaaAAaA" | ./crchack - 96 | 1337c0de 97 | 98 | [crchack]$ echo "1234567654321" | ./crchack -b .0:-1:1 -b .1:-1:1 -b .2:-1:1 - baadf00d 99 | 0713715377223 100 | [crchack]$ echo "0713715377223" | ./crchack - 101 | baadf00d 102 | ``` 103 | 104 | In the latter example, repetition can be avoided by utilizing brace expansions: 105 | 106 | ``` 107 | [crchack]$ echo "1234567654321" | ./crchack -b ".{0-2}:-1:1" - baadf00d 108 | 0713715377223 109 | ``` 110 | 111 | Obtaining the target checksum is impossible if given an insufficient number of 112 | mutable bits. In general, the user should provide at least *w* bits where *w* 113 | is the width of the CRC register, e.g., 32 bits for CRC-32. 114 | 115 | 116 | # CRC algorithms 117 | 118 | crchack works with all CRCs that use sane parameters, including all commonly 119 | used standardized CRCs. crchack defaults to CRC-32 and other CRC functions can 120 | be specified by passing the CRC parameters via command-line arguments. 121 | 122 | ``` 123 | [crchack]$ printf "123456789" > msg 124 | [crchack]$ ./crchack -w8 -p7 msg # CRC-8 125 | f4 126 | [crchack]$ ./crchack -w16 -p8005 -rR msg # CRC-16 127 | bb3d 128 | [crchack]$ ./crchack -w16 -p8005 -iffff -rR msg # MODBUS 129 | 4b37 130 | [crchack]$ ./crchack -w32 -p04c11db7 -iffffffff -xffffffff -rR msg # CRC-32 131 | cbf43926 132 | ``` 133 | 134 | [CRC RevEng](https://reveng.sourceforge.io/) (by Greg Cook) includes a 135 | comprehensive [catalogue](https://reveng.sourceforge.io/crc-catalogue/) of 136 | cyclic redundancy check algorithms and their parameters. [check.sh](check.sh) 137 | illustrates how to convert the CRC catalogue entries to crchack CLI flags. 138 | 139 | 140 | # How it works? 141 | 142 | CRC is often described as a linear function in the literature. However, CRC 143 | implementations used in practice often differ from the theoretical definition 144 | and satisfy only a *weak* linearity property (`^` denotes XOR operator): 145 | 146 | CRC(x ^ y ^ z) = CRC(x) ^ CRC(y) ^ CRC(z), for |x| = |y| = |z| 147 | 148 | crchack applies this rule to construct a system of linear equations such that 149 | the solution is a set of input bits which inverted yield the target checksum. 150 | 151 | The intuition is that each input bit flip causes a fixed difference in the 152 | resulting checksum (independent of the values of the neighbouring bits). This, 153 | in addition to knowing the required difference, produces a system of linear 154 | equations over [GF(2)](https://en.wikipedia.org/wiki/Finite_field). crchack 155 | expresses the system in a matrix form and solves it with the Gauss-Jordan 156 | elimination algorithm. 157 | 158 | Notice that the CRC function is treated as a "black box", i.e., the internal 159 | CRC parameters are used only for evaluation. Therefore, the same approach is 160 | applicable to any function that satisfies the weak linearity property. 161 | 162 | 163 | # Use cases 164 | 165 | So why would someone want to forge CRC checksums? Because `CRC32("It'S coOL To 166 | wRitE meSSAGes LikE this :DddD") == 0xDEADBEEF`. 167 | 168 | In a more serious sense, there exist a bunch of firmwares, protocols, file 169 | formats and standards that utilize CRCs. Hacking these may involve, e.g., 170 | modification of data so that the original CRC checksum remains intact. One 171 | interesting possibility is the ability to store arbitrary data (e.g., binary 172 | code) to checksum fields in packet and file headers. 173 | -------------------------------------------------------------------------------- /check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # crchack tests 3 | CRCHACK="${1:-"$(dirname "$(realpath "$0")")/crchack"}" 4 | if [ ! -x "$CRCHACK" ]; then 5 | echo "crchack not found" >&2 6 | echo "usage: $0 ./crchack" >&2 7 | exit 1 8 | fi 9 | 10 | OK=0 11 | FAIL=0 12 | expect () { 13 | if [ "$1" = "$2" ]; then 14 | OK=$((OK + 1)) 15 | printf " OK" 16 | else 17 | FAIL=$((FAIL + 1)) 18 | printf " FAIL" 19 | fi 20 | } 21 | check () { 22 | OPTS="$1" 23 | EXPECT="$2" 24 | #NAME="$3" 25 | printf "CHECK %s %s ..." "$CRCHACK" "$OPTS" 26 | expect "$EXPECT" "$(printf 123456789 | eval "$CRCHACK" "$OPTS" - | tr -d '\r\n')" 27 | expect "123456789" "$(printf 023456789 | eval "$CRCHACK" "$OPTS" -b0:1 - "$EXPECT")" 28 | expect "123456789" "$(printf 103456789 | eval "$CRCHACK" "$OPTS" -b1.1:2 - "$EXPECT")" 29 | expect "123456789" "$(printf 120456789 | eval "$CRCHACK" "$OPTS" -b2:3 - "$EXPECT")" 30 | expect "123456789" "$(printf 123056789 | eval "$CRCHACK" "$OPTS" -b3.2:4 - "$EXPECT")" 31 | printf "\n" 32 | } 33 | 34 | # curl -s https://reveng.sourceforge.io/crc-catalogue/all.htm \ 35 | # | grep -o 'width=.*name="[^"]*"' | sed 's/\s\+/ /g' \ 36 | # | sed 's/width=/-w/' | sed 's/poly=0x/-p/' | sed 's/init=0x/-i/' \ 37 | # | sed 's/refin=false //' | sed 's/refin=true/-r/' \ 38 | # | sed 's/refout=false //' | sed 's/refout=true/-R/' \ 39 | # | sed 's/xorout=0x/-x/' \ 40 | # | sed 's/ -[xi]0\+\>//g' \ 41 | # | sed 's/-r -R/-rR/g' \ 42 | # | sed 's/^\(.*\) check=0x\([0-9A-Fa-f]*\).* name="\([^"]*\)".*$/check "\1" "\2" "\3"/' 43 | check "-w3 -p3 -x7" "4" "CRC-3/GSM" 44 | check "-w3 -p3 -i7 -rR" "6" "CRC-3/ROHC" 45 | check "-w4 -p3 -rR" "7" "CRC-4/G-704" 46 | check "-w4 -p3 -if -xf" "b" "CRC-4/INTERLAKEN" 47 | check "-w5 -p09 -i09" "00" "CRC-5/EPC-C1G2" 48 | check "-w5 -p15 -rR" "07" "CRC-5/G-704" 49 | check "-w5 -p05 -i1f -rR -x1f" "19" "CRC-5/USB" 50 | check "-w6 -p27 -i3f" "0d" "CRC-6/CDMA2000-A" 51 | check "-w6 -p07 -i3f" "3b" "CRC-6/CDMA2000-B" 52 | check "-w6 -p19 -rR" "26" "CRC-6/DARC" 53 | check "-w6 -p03 -rR" "06" "CRC-6/G-704" 54 | check "-w6 -p2f -x3f" "13" "CRC-6/GSM" 55 | check "-w7 -p09" "75" "CRC-7/MMC" 56 | check "-w7 -p4f -i7f -rR" "53" "CRC-7/ROHC" 57 | check "-w7 -p45" "61" "CRC-7/UMTS" 58 | check "-w8 -p2f -iff -xff" "df" "CRC-8/AUTOSAR" 59 | check "-w8 -pa7 -rR" "26" "CRC-8/BLUETOOTH" 60 | check "-w8 -p9b -iff" "da" "CRC-8/CDMA2000" 61 | check "-w8 -p39 -rR" "15" "CRC-8/DARC" 62 | check "-w8 -pd5" "bc" "CRC-8/DVB-S2" 63 | check "-w8 -p1d" "37" "CRC-8/GSM-A" 64 | check "-w8 -p49 -xff" "94" "CRC-8/GSM-B" 65 | check "-w8 -p1d -iff" "b4" "CRC-8/HITAG" 66 | check "-w8 -p07 -x55" "a1" "CRC-8/I-432-1" 67 | check "-w8 -p1d -ifd" "7e" "CRC-8/I-CODE" 68 | check "-w8 -p9b" "ea" "CRC-8/LTE" 69 | check "-w8 -p31 -rR" "a1" "CRC-8/MAXIM-DOW" 70 | check "-w8 -p1d -ic7" "99" "CRC-8/MIFARE-MAD" 71 | check "-w8 -p31 -iff" "f7" "CRC-8/NRSC-5" 72 | check "-w8 -p2f" "3e" "CRC-8/OPENSAFETY" 73 | check "-w8 -p07 -iff -rR" "d0" "CRC-8/ROHC" 74 | check "-w8 -p1d -iff -xff" "4b" "CRC-8/SAE-J1850" 75 | check "-w8 -p07" "f4" "CRC-8/SMBUS" 76 | check "-w8 -p1d -iff -rR" "97" "CRC-8/TECH-3250" 77 | check "-w8 -p9b -rR" "25" "CRC-8/WCDMA" 78 | check "-w10 -p233" "199" "CRC-10/ATM" 79 | check "-w10 -p3d9 -i3ff" "233" "CRC-10/CDMA2000" 80 | check "-w10 -p175 -x3ff" "12a" "CRC-10/GSM" 81 | check "-w11 -p385 -i01a" "5a3" "CRC-11/FLEXRAY" 82 | check "-w11 -p307" "061" "CRC-11/UMTS" 83 | check "-w12 -pf13 -ifff" "d4d" "CRC-12/CDMA2000" 84 | check "-w12 -p80f" "f5b" "CRC-12/DECT" 85 | check "-w12 -pd31 -xfff" "b34" "CRC-12/GSM" 86 | check "-w12 -p80f -R" "daf" "CRC-12/UMTS" 87 | check "-w13 -p1cf5" "04fa" "CRC-13/BBC" 88 | check "-w14 -p0805 -rR" "082d" "CRC-14/DARC" 89 | check "-w14 -p202d -x3fff" "30ae" "CRC-14/GSM" 90 | check "-w15 -p4599" "059e" "CRC-15/CAN" 91 | check "-w15 -p6815 -x0001" "2566" "CRC-15/MPT1327" 92 | check "-w16 -p8005 -rR" "bb3d" "CRC-16/ARC" 93 | check "-w16 -pc867 -iffff" "4c06" "CRC-16/CDMA2000" 94 | check "-w16 -p8005 -iffff" "aee7" "CRC-16/CMS" 95 | check "-w16 -p8005 -i800d" "9ecf" "CRC-16/DDS-110" 96 | check "-w16 -p0589 -x0001" "007e" "CRC-16/DECT-R" 97 | check "-w16 -p0589" "007f" "CRC-16/DECT-X" 98 | check "-w16 -p3d65 -rR -xffff" "ea82" "CRC-16/DNP" 99 | check "-w16 -p3d65 -xffff" "c2b7" "CRC-16/EN-13757" 100 | check "-w16 -p1021 -iffff -xffff" "d64e" "CRC-16/GENIBUS" 101 | check "-w16 -p1021 -xffff" "ce3c" "CRC-16/GSM" 102 | check "-w16 -p1021 -iffff" "29b1" "CRC-16/IBM-3740" 103 | check "-w16 -p1021 -iffff -rR -xffff" "906e" "CRC-16/IBM-SDLC" 104 | check "-w16 -p1021 -ic6c6 -rR" "bf05" "CRC-16/ISO-IEC-14443-3-A" 105 | check "-w16 -p1021 -rR" "2189" "CRC-16/KERMIT" 106 | check "-w16 -p6f63" "bdf4" "CRC-16/LJ1200" 107 | check "-w16 -p5935 -iffff" "772b" "CRC-16/M17" 108 | check "-w16 -p8005 -rR -xffff" "44c2" "CRC-16/MAXIM-DOW" 109 | check "-w16 -p1021 -iffff -rR" "6f91" "CRC-16/MCRF4XX" 110 | check "-w16 -p8005 -iffff -rR" "4b37" "CRC-16/MODBUS" 111 | check "-w16 -p080b -iffff -rR" "a066" "CRC-16/NRSC-5" 112 | check "-w16 -p5935" "5d38" "CRC-16/OPENSAFETY-A" 113 | check "-w16 -p755b" "20fe" "CRC-16/OPENSAFETY-B" 114 | check "-w16 -p1dcf -iffff -xffff" "a819" "CRC-16/PROFIBUS" 115 | check "-w16 -p1021 -ib2aa -rR" "63d0" "CRC-16/RIELLO" 116 | check "-w16 -p1021 -i1d0f" "e5cc" "CRC-16/SPI-FUJITSU" 117 | check "-w16 -p8bb7" "d0db" "CRC-16/T10-DIF" 118 | check "-w16 -pa097" "0fb3" "CRC-16/TELEDISK" 119 | check "-w16 -p1021 -i89ec -rR" "26b1" "CRC-16/TMS37157" 120 | check "-w16 -p8005" "fee8" "CRC-16/UMTS" 121 | check "-w16 -p8005 -iffff -rR -xffff" "b4c8" "CRC-16/USB" 122 | check "-w16 -p1021" "31c3" "CRC-16/XMODEM" 123 | check "-w17 -p1685b" "04f03" "CRC-17/CAN-FD" 124 | check "-w21 -p102899" "0ed841" "CRC-21/CAN-FD" 125 | check "-w24 -p00065b -i555555 -rR" "c25a56" "CRC-24/BLE" 126 | check "-w24 -p5d6dcb -ifedcba" "7979bd" "CRC-24/FLEXRAY-A" 127 | check "-w24 -p5d6dcb -iabcdef" "1f23b8" "CRC-24/FLEXRAY-B" 128 | check "-w24 -p328b63 -iffffff -xffffff" "b4f3e6" "CRC-24/INTERLAKEN" 129 | check "-w24 -p864cfb" "cde703" "CRC-24/LTE-A" 130 | check "-w24 -p800063" "23ef52" "CRC-24/LTE-B" 131 | check "-w24 -p864cfb -ib704ce" "21cf02" "CRC-24/OPENPGP" 132 | check "-w24 -p800063 -iffffff -xffffff" "200fa5" "CRC-24/OS-9" 133 | check "-w30 -p2030b9c7 -i3fffffff -x3fffffff" "04c34abf" "CRC-30/CDMA" 134 | check "-w31 -p04c11db7 -i7fffffff -x7fffffff" "0ce9e46c" "CRC-31/PHILIPS" 135 | check "-w32 -p814141ab" "3010bf7f" "CRC-32/AIXM" 136 | check "-w32 -pf4acfb13 -iffffffff -rR -xffffffff" "1697d06a" "CRC-32/AUTOSAR" 137 | check "-w32 -pa833982b -iffffffff -rR -xffffffff" "87315576" "CRC-32/BASE91-D" 138 | check "-w32 -p04c11db7 -iffffffff -xffffffff" "fc891918" "CRC-32/BZIP2" 139 | check "-w32 -p8001801b -rR" "6ec2edc4" "CRC-32/CD-ROM-EDC" 140 | check "-w32 -p04c11db7 -xffffffff" "765e7680" "CRC-32/CKSUM" 141 | check "-w32 -p1edc6f41 -iffffffff -rR -xffffffff" "e3069283" "CRC-32/ISCSI" 142 | check "-w32 -p04c11db7 -iffffffff -rR -xffffffff" "cbf43926" "CRC-32/ISO-HDLC" 143 | check "-w32 -p04c11db7 -iffffffff -rR" "340bc6d9" "CRC-32/JAMCRC" 144 | check "-w32 -p741b8cd7 -iffffffff -rR" "d2c22f51" "CRC-32/MEF" 145 | check "-w32 -p04c11db7 -iffffffff" "0376e6e7" "CRC-32/MPEG-2" 146 | check "-w32 -p000000af" "bd0be338" "CRC-32/XFER" 147 | check "-w40 -p0004820009 -xffffffffff" "d4164fc646" "CRC-40/GSM" 148 | check "-w64 -p42f0e1eba9ea3693" "6c40df5f0b497347" "CRC-64/ECMA-182" 149 | check "-w64 -p000000000000001b -iffffffffffffffff -rR -xffffffffffffffff" "b90956c775a41001" "CRC-64/GO-ISO" 150 | check "-w64 -p259c84cba6426349 -iffffffffffffffff -rR" "75d4b74f024eceea" "CRC-64/MS" 151 | check "-w64 -pad93d23594c935a9 -rR" "e9c6d914c4b8d9ca" "CRC-64/REDIS" 152 | check "-w64 -p42f0e1eba9ea3693 -iffffffffffffffff -xffffffffffffffff" "62ec59e3f1a4f00a" "CRC-64/WE" 153 | check "-w64 -p42f0e1eba9ea3693 -iffffffffffffffff -rR -xffffffffffffffff" "995dc9bbdf1939fa" "CRC-64/XZ" 154 | check "-w82 -p0308c0111011401440411 -rR" "09ea83f625023801fd612" "CRC-82/DARC" 155 | 156 | printf 'SOLVE %s Google CTF 2018 (Quals) task "Tape, misc, 355p" ...' "$CRCHACK" 157 | expect ': You probably just want the flag. So here it is: CTF{dZXicOXLaMumrTPIUTYMI}. :' "$(printf ': You probably just want the flag. So here it is: CTF{dZXi__________PIUTYMI}. :' | eval "$CRCHACK" -b '59.{0-5}:69:1' -w64 -p0x42F0E1EBA9EA3693 -rR - 0x30d498cbfb871112)" 158 | expect "30d498cbfb871112" "$(printf ': You probably just want the flag. So here it is: CTF{dZXi__________PIUTYMI}. :' | eval "$CRCHACK" -b '59.{0-5}:69:1' -w64 -p0x42F0E1EBA9EA3693 -rR - 0x30d498cbfb871112 | eval "$CRCHACK" -w64 -p0x42F0E1EBA9EA3693 -rR -)" 159 | printf "\n" 160 | -------------------------------------------------------------------------------- /crc.c: -------------------------------------------------------------------------------- 1 | #include "crc.h" 2 | 3 | static const uint8_t bytebits[2][8] = { 4 | { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* MSB to LSB */ 5 | { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 } /* LSB to MSB */ 6 | }; 7 | 8 | void crc_bits(const struct crc_config *crc, 9 | const void *msg, bitsize_t i, bitsize_t j, 10 | struct bigint *checksum) 11 | { 12 | /* Input bytes */ 13 | const uint8_t *bytes = msg; 14 | 15 | /* Reflect input bytes */ 16 | const uint8_t *bits = bytebits[crc->reflect_in]; 17 | 18 | /* Initial XOR value */ 19 | bigint_xor(checksum, &crc->init); 20 | 21 | /* Process input bits */ 22 | while (i < j) { 23 | int bit = bigint_msb(checksum) ^ !!(bytes[i / 8] & bits[i % 8]); 24 | bigint_shl_1(checksum); 25 | if (bit) bigint_xor(checksum, &crc->poly); 26 | i++; 27 | } 28 | 29 | /* Final XOR mask */ 30 | bigint_xor(checksum, &crc->xor_out); 31 | 32 | /* Reflect output */ 33 | if (crc->reflect_out) 34 | bigint_reflect(checksum); 35 | } 36 | 37 | void crc(const struct crc_config *crc, const void *msg, size_t len, 38 | struct bigint *checksum) 39 | { 40 | bigint_load_zeros(checksum); 41 | crc_bits(crc, msg, 0, 8 * (bitsize_t)len, checksum); 42 | } 43 | 44 | void crc_append_bits(const struct crc_config *crc, 45 | const void *msg, bitsize_t i, bitsize_t j, 46 | struct bigint *checksum) 47 | { 48 | if (crc->reflect_out) 49 | bigint_reflect(checksum); 50 | bigint_xor(checksum, &crc->xor_out); 51 | bigint_xor(checksum, &crc->init); 52 | crc_bits(crc, msg, i, j, checksum); 53 | } 54 | 55 | void crc_append(const struct crc_config *crc, const void *msg, size_t len, 56 | struct bigint *checksum) 57 | { 58 | crc_append_bits(crc, msg, 0, 8 * (bitsize_t)len, checksum); 59 | } 60 | 61 | /* 62 | * CRC sparse engine 63 | */ 64 | #include 65 | #include 66 | 67 | /* A = B */ 68 | struct bigint *bitmatrix_mov(struct bigint *A, const struct bigint *B) 69 | { 70 | size_t i; 71 | const size_t w = A[0].bits; 72 | for (i = 0; i < w; i++) 73 | bigint_mov(&A[i], &B[i]); 74 | return A; 75 | } 76 | 77 | /* Solve AX = B (upon return, A=I and B=X) */ 78 | static int bitmatrix_solve(struct bigint *A, struct bigint *B, const size_t w) 79 | { 80 | size_t i, j; 81 | for (i = 0; i < w; i++) { 82 | for (j = i; j < w; j++) { 83 | if (bigint_get_bit(&A[j], i)) { 84 | bigint_swap(&A[i], &A[j]); 85 | bigint_swap(&B[i], &B[j]); 86 | break; 87 | } 88 | } 89 | if (j == w) 90 | break; 91 | for (j = 0; j < w; j++) { 92 | if (i != j && bigint_get_bit(&A[j], i)) { 93 | bigint_xor(&A[j], &A[i]); 94 | bigint_xor(&B[j], &B[i]); 95 | } 96 | } 97 | } 98 | return i == w; 99 | } 100 | 101 | /* X = AB */ 102 | static struct bigint * 103 | bitmatrix_mul(const struct bigint *A, const struct bigint *B, struct bigint *X) 104 | { 105 | size_t i, j; 106 | const size_t w = A->bits; 107 | for (i = 0; i < w; i++) { 108 | bigint_load_zeros(&X[i]); 109 | for (j = 0; j < w; j++) { 110 | if (bigint_get_bit(&A[i], j)) 111 | bigint_xor(&X[i], &B[j]); 112 | } 113 | } 114 | return X; 115 | } 116 | 117 | /* New CRC calculator engine for sparse inputs and size-bit long message */ 118 | struct crc_sparse *crc_sparse_new(const struct crc_config *crc, bitsize_t size) 119 | { 120 | uint8_t *buf; 121 | bitsize_t i, j, m, n; 122 | struct crc_sparse *engine; 123 | struct bigint *D, *L, *R, *PQ, z; 124 | const size_t w = crc->width; 125 | const uint8_t *bits = bytebits[crc->reflect_in]; 126 | 127 | /* Special case for short messages */ 128 | if (size < w) { 129 | if (!(engine = malloc(sizeof(struct crc_sparse) + (w / 8) + !!(w % 8)))) 130 | return NULL; 131 | memcpy(&engine->crc, crc, sizeof(struct crc_config)); 132 | engine->size = size; 133 | engine->D = engine->L = engine->R = NULL; 134 | memset((char *)engine + sizeof(struct crc_sparse), 0, (w / 8) + !!(w % 8)); 135 | return engine; 136 | } 137 | 138 | /* Calculate size for L R matrix tables */ 139 | for (m = 0, i = w; i; i >>= 1, m++); 140 | for (n = 0, i = size; i; i >>= 1, n++); 141 | 142 | /* Allocate engine and working memory */ 143 | engine = malloc(sizeof(struct crc_sparse)); 144 | D = engine ? bigint_array_new((1 + 2 * n + 2) * w, w) : NULL; 145 | buf = D ? calloc(sizeof(uint8_t), ((2*w) / 8) + !!((2*w) % 8)) : NULL; 146 | if (!buf || !bigint_init(&z, w)) { 147 | free(buf); 148 | bigint_array_delete(D); 149 | free(engine); 150 | return NULL; 151 | } 152 | memcpy(&engine->crc, crc, sizeof(struct crc_config)); 153 | engine->size = size; 154 | engine->D = D; 155 | engine->L = L = &D[1 * w]; 156 | engine->R = R = &L[n * w]; 157 | engine->PQ = PQ = &R[n * w]; 158 | 159 | /* Calculate D (differences of bit flips for a w-bit window) */ 160 | crc_bits(crc, buf, 0, w, &z); 161 | for (i = 0; i < w; i++) { 162 | buf[i / 8] ^= bits[i % 8]; 163 | crc_bits(crc, buf, 0, w, &D[i]); 164 | bigint_xor(&D[i], &z); 165 | buf[i / 8] ^= bits[i % 8]; 166 | } 167 | 168 | /* Solve AL = B and BR = A for power-of-2 moves up to w bits */ 169 | for (j = 0; j < m; j++) { 170 | size_t s = (size_t)1 << j; 171 | bigint_load_zeros(&z); 172 | crc_bits(crc, buf, 0, w + s, &z); 173 | for (i = 0; i < w; i++) { 174 | buf[(s + i) / 8] ^= bits[(s + i) % 8]; 175 | crc_bits(crc, buf, 0, w + s, &L[j*w + i]); 176 | bigint_xor(&L[j*w + i], &z); 177 | buf[(s + i) / 8] ^= bits[(s + i) % 8]; 178 | } 179 | if (!bitmatrix_solve(bitmatrix_mov(PQ, D), &L[j*w], w)) 180 | break; 181 | for (i = 0; i < w; i++) { 182 | buf[i / 8] ^= bits[i % 8]; 183 | crc_bits(crc, buf, 0, w + s, &R[j*w + i]); 184 | bigint_xor(&R[j*w + i], &z); 185 | buf[i / 8] ^= bits[i % 8]; 186 | } 187 | if (!bitmatrix_solve(bitmatrix_mov(PQ, D), &R[j*w], w)) 188 | break; 189 | } 190 | free(buf); 191 | bigint_destroy(&z); 192 | if (j < m) { 193 | crc_sparse_delete(engine); 194 | return NULL; 195 | } 196 | 197 | /* Remaining L/R moves by squaring */ 198 | while (j < n) { 199 | bitmatrix_mul(&L[(j-1)*w], &L[(j-1)*w], &L[j*w]); 200 | bitmatrix_mul(&R[(j-1)*w], &R[(j-1)*w], &R[j*w]); 201 | j++; 202 | } 203 | 204 | return engine; 205 | } 206 | 207 | /* Adjust CRC checksum for a message with bit flip in the given position */ 208 | int crc_sparse_1bit(struct crc_sparse *engine, bitsize_t pos, 209 | struct bigint *checksum) 210 | { 211 | struct bigint *P, *Q, *PQ; 212 | bitsize_t ldist, rdist, i; 213 | const bitsize_t w = engine->crc.width; 214 | const uint8_t *bits = bytebits[engine->crc.reflect_in]; 215 | if (pos >= engine->size || checksum->bits != w) 216 | return 0; 217 | 218 | /* Naive algorithm for short messages (engine->D unset) */ 219 | if (!engine->D) { 220 | struct bigint x; 221 | unsigned char *buf; 222 | if (!bigint_init(&x, w)) 223 | return 0; 224 | buf = (unsigned char *)engine + sizeof(struct crc_sparse); 225 | crc_bits(&engine->crc, buf, 0, engine->size, &x); 226 | bigint_xor(checksum, &x); 227 | bigint_load_zeros(&x); 228 | buf[pos / 8] ^= bits[pos % 8]; 229 | crc_bits(&engine->crc, buf, 0, engine->size, &x); 230 | bigint_xor(checksum, &x); 231 | buf[pos / 8] ^= bits[pos % 8]; 232 | bigint_destroy(&x); 233 | return 1; 234 | } 235 | 236 | /* Work space */ 237 | PQ = engine->PQ; 238 | P = &PQ[0]; 239 | Q = &PQ[w]; 240 | 241 | /* ldist + w + rdist == size */ 242 | ldist = (pos < w) ? 0 : pos - (w-1); 243 | rdist = engine->size - (ldist + w); 244 | 245 | /* P = D */ 246 | bitmatrix_mov(P, engine->D); 247 | 248 | /* Left moves */ 249 | for (i = 0; ldist; i++) { 250 | if (ldist & 1) 251 | PQ = bitmatrix_mul(PQ, &engine->L[i*w], (PQ == P) ? Q : P); 252 | ldist >>= 1; 253 | } 254 | 255 | /* Right moves */ 256 | for (i = 0; rdist; i++) { 257 | if (rdist & 1) 258 | PQ = bitmatrix_mul(PQ, &engine->R[i*w], (PQ == P) ? Q : P); 259 | rdist >>= 1; 260 | } 261 | 262 | bigint_xor(checksum, &PQ[(pos < w) ? pos : w-1]); 263 | return 1; 264 | } 265 | 266 | /* Delete CRC sparse engine */ 267 | void crc_sparse_delete(struct crc_sparse *engine) 268 | { 269 | if (engine) { 270 | bigint_array_delete(engine->D); 271 | free(engine); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /crchack.c: -------------------------------------------------------------------------------- 1 | #define __USE_MINGW_ANSI_STDIO 1 /* make MinGW happy */ 2 | #include "bigint.h" 3 | #include "crc.h" 4 | #include "forge.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | static void help(char *argv0) 14 | { 15 | fprintf(stderr, "usage: %s [options] file [target_checksum]\n", argv0); 16 | fprintf(stderr, "\n" 17 | "options:\n" 18 | " -o pos byte.bit position of mutable input bits\n" 19 | " -O pos position offset from the end of the input\n" 20 | " -b l:r:s specify bits at positions l..r with step s\n" 21 | " -h show this help\n" 22 | " -v verbose mode\n" 23 | "\n" 24 | "CRC parameters (default: CRC-32):\n" 25 | " -p poly generator polynomial -w size register size in bits\n" 26 | " -i init initial register value -x xor final register XOR mask\n" 27 | " -r reverse input bytes -R reverse final register\n"); 28 | } 29 | 30 | /* 31 | * suckopts(): POSIXish minimal getopt(3) implementation. 32 | */ 33 | static int suckind = 1; 34 | static int suckpos = 0; 35 | static int suckopt; 36 | static char *suckarg; 37 | static int suckopts(int argc, char * const argv[], const char *suckstring) 38 | { 39 | const char *p; 40 | suckopt = 0; 41 | suckarg = (char *)0; 42 | if (!suckind) { 43 | suckpos = 0; 44 | suckind = 1; 45 | } 46 | 47 | if (!suckpos) { 48 | if (suckind < argc && argv[suckind]) { 49 | if (argv[suckind][0] == '-' && argv[suckind][1]) { 50 | if (argv[suckind][1] != '-') { 51 | suckpos = 1; 52 | } else if (!argv[suckind][2]) { 53 | suckopt = '-'; 54 | suckind++; 55 | } 56 | } else if (suckstring[0] == '-') { 57 | suckarg = argv[suckind++]; 58 | return 1; 59 | } 60 | } 61 | if (!suckpos) 62 | return -1; 63 | } 64 | 65 | suckopt = argv[suckind][suckpos++]; 66 | if (!argv[suckind][suckpos]) { 67 | suckpos = 0; 68 | suckind++; 69 | } 70 | 71 | if (suckstring[0] == '-' || suckstring[0] == '+') 72 | suckstring++; 73 | for (p = suckstring; *p && (*p == ':' || *p != suckopt); p++); 74 | if (p[0] != suckopt) 75 | return '?'; 76 | 77 | if (p[1] == ':') { 78 | if (p[2] != ':' || suckpos) { 79 | if (suckind >= argc) 80 | return "?:"[suckstring[0] == ':']; 81 | suckarg = argv[suckind++] + suckpos; 82 | suckpos = 0; 83 | } 84 | } 85 | return suckopt; 86 | } 87 | 88 | /* 89 | * Python-style slices (left:right:step) representing a slice of bit indices. 90 | */ 91 | struct slice { 92 | bitoffset_t l; 93 | bitoffset_t r; 94 | bitoffset_t s; 95 | int relative; /* non-zero if r is relative to l */ 96 | }; 97 | 98 | /* 99 | * Extract at most `limit` bits defined by a slice into an output array `bits`. 100 | * The return value is the total number of extracted bits. 101 | */ 102 | static bitsize_t bits_of_slice(struct slice *slice, 103 | bitsize_t end, bitsize_t limit, 104 | bitsize_t *bits) 105 | { 106 | bitsize_t n; 107 | bitoffset_t l = slice->l, r = slice->r, s = slice->s; 108 | if (s == 0) { 109 | fprintf(stderr, "slice step cannot be zero\n"); 110 | return 0; 111 | } 112 | 113 | if (l < 0 && (l += end) < 0) l = 0; 114 | else if ((bitsize_t)l > end) l = end; 115 | 116 | if (slice->relative) r += l; 117 | 118 | if (r < 0 && (r += end) < 0) r = 0; 119 | else if ((bitsize_t)r > end) r = end; 120 | 121 | n = 0; 122 | while ((!limit || n < limit) && ((s > 0 && l < r) || (s < 0 && l > r))) { 123 | if (bits) *bits++ = l; 124 | l += s; 125 | n++; 126 | } 127 | 128 | return n; 129 | } 130 | 131 | /* 132 | * User input and command-line options (filled by handle_args()). 133 | */ 134 | static struct { 135 | char *filename; 136 | FILE *in; 137 | FILE *out; 138 | 139 | size_t len; 140 | bitsize_t bitlen; 141 | size_t pad; 142 | struct bigint checksum; 143 | 144 | struct crc_config crc; 145 | struct crc_sparse *sparse; 146 | struct bigint target; 147 | int has_target; 148 | 149 | bitsize_t *bits; 150 | bitsize_t nbits; 151 | 152 | struct slice *slices; 153 | size_t nslices; 154 | 155 | int verbose; 156 | } input; 157 | 158 | /* 159 | * Forward declarations for handle_args(). 160 | */ 161 | static int peek(const char **pp); 162 | static int accept(const char **pp, char ch); 163 | static int accept_any(const char **pp, const char *set); 164 | 165 | static int parse_offset(const char *p, bitoffset_t *offset); 166 | static int parse_slice(const char *p, struct slice *slice); 167 | 168 | static int handle_slice_option(const char *slice); 169 | static FILE *handle_message_file(const char *filename, size_t *size); 170 | 171 | /* 172 | * Parse command-line arguments. 173 | * 174 | * Returns an exit code (0 for success). 175 | */ 176 | static int handle_args(int argc, char *argv[]) 177 | { 178 | bitsize_t nbits; 179 | bitoffset_t offset; 180 | int c, has_offset; 181 | size_t i, j, width; 182 | char *poly, *init, reflect_in, reflect_out, *xor_out, *target; 183 | 184 | offset = 0; 185 | has_offset = 0; 186 | target = NULL; 187 | 188 | /* CRC parameters */ 189 | width = 0; 190 | poly = init = xor_out = NULL; 191 | reflect_in = reflect_out = 0; 192 | memset(&input, 0, sizeof(input)); 193 | 194 | /* Parse command options */ 195 | while ((c = suckopts(argc, argv, ":hvp:w:i:x:rRo:O:b:")) != -1) { 196 | switch (c) { 197 | case 'h': help(argv[0]); return 1; 198 | case 'v': input.verbose++; break; 199 | case 'p': poly = suckarg; break; 200 | case 'w': 201 | if (sscanf(suckarg, "%zu", &width) != 1) { 202 | fprintf(stderr, "invalid CRC width '%s'\n", suckarg); 203 | return 1; 204 | } 205 | break; 206 | case 'i': init = suckarg; break; 207 | case 'x': xor_out = suckarg; break; 208 | case 'r': reflect_in = 1; break; 209 | case 'R': reflect_out = 1; break; 210 | case 'o': case 'O': 211 | if (has_offset) { 212 | fprintf(stderr, "multiple -oO not allowed\n"); 213 | return 1; 214 | } 215 | if (parse_offset(suckarg, &offset)) { 216 | const char *p = suckarg; 217 | if (!offset && peek(&p) == '-') 218 | c ^= 'o' ^ 'O'; /* Swap -o/-O to handle negative zero */ 219 | has_offset = c; 220 | } else { 221 | fprintf(stderr, "invalid offset '%s'\n", suckarg); 222 | return 1; 223 | } 224 | break; 225 | case 'b': 226 | if (!handle_slice_option(suckarg)) 227 | return 1; 228 | break; 229 | 230 | case ':': 231 | fprintf(stderr, "option -%c requires an argument\n", suckopt); 232 | return 1; 233 | case '?': 234 | if (isprint(suckopt)) { 235 | fprintf(stderr, "unknown option -%c\n", suckopt); 236 | } else { 237 | fprintf(stderr, "unknown option \"\\x%02X\"\n", suckopt); 238 | } 239 | return 1; 240 | default: 241 | help(argv[0]); 242 | return 1; 243 | } 244 | } 245 | 246 | /* Determine input file argument position */ 247 | if (suckind == argc || suckind+2 < argc) { 248 | help(argv[0]); 249 | return 1; 250 | } 251 | input.filename = argv[suckind]; 252 | target = (suckind == argc-2) ? argv[argc-1] : NULL; 253 | 254 | /* CRC parameters */ 255 | if (!width && poly) { 256 | const char *p = poly + ((poly[0] == '0' && poly[1] == 'x') << 1); 257 | size_t span = strspn(p, "0123456789abcdefABCDEF"); 258 | if (!span || p[span] != '\0') { 259 | fprintf(stderr, "invalid poly (%s)\n", poly); 260 | return 1; 261 | } 262 | width = span * 4; 263 | } 264 | input.crc.width = width ? width : 32; 265 | bigint_init(&input.crc.poly, input.crc.width); 266 | bigint_init(&input.crc.init, input.crc.width); 267 | bigint_init(&input.crc.xor_out, input.crc.width); 268 | if (width || poly || init || xor_out || reflect_in || reflect_out) { 269 | if (!poly) { 270 | fprintf(stderr, "custom CRC requires generator polynomial\n"); 271 | return 1; 272 | } 273 | 274 | /* CRC generator polynomial */ 275 | if (!bigint_from_string(&input.crc.poly, poly)) { 276 | fprintf(stderr, "invalid poly (%s)\n", poly); 277 | return 1; 278 | } 279 | 280 | /* Initial CRC register value */ 281 | if (init && !bigint_from_string(&input.crc.init, init)) { 282 | fprintf(stderr, "invalid init (%s)\n", init); 283 | return 1; 284 | } 285 | 286 | /* Final CRC register XOR mask */ 287 | if (xor_out && !bigint_from_string(&input.crc.xor_out, xor_out)) { 288 | fprintf(stderr, "invalid xor_out (%s)\n", xor_out); 289 | return 1; 290 | } 291 | 292 | /* Reflect in and out */ 293 | input.crc.reflect_in = reflect_in; 294 | input.crc.reflect_out = reflect_out; 295 | } else { 296 | /* Default: CRC-32 */ 297 | bigint_from_string(&input.crc.poly, "04c11db7"); 298 | bigint_load_ones(&input.crc.init); 299 | bigint_load_ones(&input.crc.xor_out); 300 | input.crc.reflect_in = 1; 301 | input.crc.reflect_out = 1; 302 | } 303 | 304 | /* Read target checksum value */ 305 | if (target) { 306 | bigint_init(&input.target, input.crc.width); 307 | if (!bigint_from_string(&input.target, target)) { 308 | bigint_destroy(&input.target); 309 | fprintf(stderr, "target checksum '%s' invalid %d-bit hex string\n", 310 | target, input.crc.width); 311 | return 1; 312 | } 313 | input.has_target = 1; 314 | } 315 | 316 | /* Read input message */ 317 | if (!(input.in = handle_message_file(input.filename, &input.len))) 318 | return 2; 319 | input.out = stdout; 320 | input.bitlen = 8 * (bitsize_t)input.len; 321 | 322 | /* Verbose message info */ 323 | if (input.verbose >= 1) { 324 | fprintf(stderr, "len(msg)"); 325 | fprintf(stderr, " = %zu bytes", input.len); 326 | fprintf(stderr, " = %ju bits\n", input.bitlen); 327 | fprintf(stderr, "CRC(msg) = "); 328 | bigint_fprint(stderr, &input.checksum); 329 | fprintf(stderr, "\n"); 330 | } 331 | 332 | /* Remaining flags are required only for forging */ 333 | if (!input.has_target) { 334 | if (has_offset) fprintf(stderr, "flags -oO ignored\n"); 335 | if (input.slices) fprintf(stderr, "flag -b ignored\n"); 336 | return 0; 337 | } 338 | 339 | /* Determine (upper bound for) size of the input.bits array */ 340 | nbits = (has_offset || !input.nslices) ? input.crc.width : 0; 341 | for (i = 0; i < input.nslices; i++) { 342 | nbits += bits_of_slice(&input.slices[i], input.bitlen, input.crc.width, 343 | NULL); 344 | } 345 | 346 | /* Fill input.bits */ 347 | if (nbits) { 348 | /* Read bit indices from '-b' slices */ 349 | if (!(input.bits = calloc(nbits, sizeof(bitsize_t)))) { 350 | fprintf(stderr, "error allocating bits array\n"); 351 | return 4; 352 | } 353 | 354 | for (i = 0; i < input.nslices; i++) { 355 | input.nbits += bits_of_slice( 356 | &input.slices[i], input.bitlen, input.crc.width, 357 | &input.bits[input.nbits] 358 | ); 359 | } 360 | 361 | /* Handle '-oO' offsets */ 362 | if (has_offset || !input.slices) { 363 | int negative = has_offset != 'o'; 364 | if (offset < 0) { 365 | negative = !negative; 366 | offset = -offset; 367 | } 368 | if (negative) { 369 | if (input.bitlen < (bitsize_t)offset) { 370 | fprintf(stderr, "offset '-%c ", has_offset); 371 | if (has_offset == 'o') fprintf(stderr, "-"); 372 | fprintf(stderr, "%jd", offset / 8); 373 | if (offset % 8) fprintf(stderr, ".%jd", offset % 8); 374 | fprintf(stderr, "' starts %ju bits before the message\n", 375 | (bitsize_t)offset - input.bitlen); 376 | return 3; 377 | } 378 | offset = input.bitlen - offset; 379 | } 380 | for (i = 0; i < input.crc.width; i++) 381 | input.bits[input.nbits++] = offset + i; 382 | } 383 | } 384 | 385 | /* Verbose bits info */ 386 | if (input.verbose >= 1) { 387 | fprintf(stderr, "bits[%zu] = {", input.nbits); 388 | for (i = 0; i < input.nbits; i++) { 389 | const char *fmt = &", %ju.%ju"[!i]; 390 | fprintf(stderr, fmt, input.bits[i]/8, input.bits[i]%8); 391 | } 392 | fprintf(stderr, " }\n"); 393 | } 394 | 395 | /* Validate bit indices and pad the message buffer if needed */ 396 | if (input.nbits) { 397 | for (i = j = 0; i < input.nbits; i++) { 398 | if (input.bits[i] >= input.bitlen + input.crc.width) { 399 | fprintf(stderr, "bits[%zu]=%ju exceeds message length (%ju bits)\n", 400 | i, input.bits[i], input.bitlen + input.crc.width); 401 | return 3; 402 | } 403 | 404 | if (input.bits[i] > input.bits[j]) 405 | j = i; 406 | } 407 | 408 | if (input.bits[j] >= input.bitlen) { 409 | size_t left; 410 | uint8_t padding[256]; 411 | memset(padding, 0, sizeof(padding)); 412 | input.pad = 1 + (input.bits[j] - input.bitlen) / 8; 413 | input.bitlen = 8 * (bitsize_t)(input.len += input.pad); 414 | left = input.pad; 415 | while (left > 0) { 416 | size_t n = (left < sizeof(padding)) ? left : sizeof(padding); 417 | crc_append(&input.crc, padding, n, &input.checksum); 418 | left -= n; 419 | } 420 | if (input.verbose >= 1) 421 | fprintf(stderr, "input message padded by %zu bytes\n", input.pad); 422 | } 423 | } 424 | 425 | /* Create sparse CRC calculation engine */ 426 | if (!(input.sparse = crc_sparse_new(&input.crc, input.bitlen))) { 427 | fputs("error initializing sparse CRC engine (bad params?)\n", stderr); 428 | return 5; 429 | } 430 | 431 | return 0; 432 | } 433 | 434 | /* 435 | * Recursive descent parser for slices (-b). 436 | */ 437 | static int peek(const char **pp) 438 | { 439 | while (**pp == ' ') (*pp)++; 440 | return **pp; 441 | } 442 | 443 | static int accept(const char **pp, char ch) 444 | { 445 | return peek(pp) == ch ? *(*pp)++ : 0; 446 | } 447 | 448 | static int accept_any(const char **pp, const char *set) 449 | { 450 | int ch = peek(pp); 451 | while (*set && ch != *set) set++; 452 | return *set ? accept(pp, ch) : 0; 453 | } 454 | 455 | static const char *parse_expression(const char *p, bitoffset_t *value); 456 | static const char *parse_factor(const char *p, bitoffset_t *value) 457 | { 458 | int dot = accept(&p, '.'); 459 | 460 | switch (peek(&p)) { 461 | case '0': case'1': case '2': case '3': case '4': 462 | case '5': case'6': case '7': case '8': case '9': 463 | errno = 0; 464 | if (p[0] == '0' && p[1] == 'x') { 465 | *value = (bitoffset_t)strtoull(p + 2, (char **)&p, 16); 466 | if (errno) perror("invalid unsigned hex integer"); 467 | } else { 468 | *value = (bitoffset_t)strtoll(p, (char **)&p, 10); 469 | if (errno) perror("invalid signed integer"); 470 | } 471 | if (errno) { 472 | p = NULL; 473 | } else if (!p) { 474 | fprintf(stderr, "invalid integer\n"); 475 | } else if (!dot) { 476 | *value *= 8; 477 | if (peek(&p) == '.') { 478 | bitoffset_t bits; 479 | if ((p = parse_factor(p, &bits))) 480 | *value += bits; 481 | } 482 | } 483 | break; 484 | 485 | case '(': 486 | if (!dot) { 487 | if ((p = parse_expression(p+1, value)) && !accept(&p, ')')) { 488 | int i; 489 | for (i = 0; p[i] && p[i] != ')'; i++); 490 | if (p[i] == ')') { 491 | fprintf(stderr, "junk before ')': '%.*s'\n", i, p); 492 | } else { 493 | fprintf(stderr, "missing parenthesis ')'\n"); 494 | } 495 | p = NULL; 496 | } 497 | break; 498 | } 499 | /* fall-through */ 500 | 501 | default: 502 | if (isprint(*p)) { 503 | fprintf(stderr, "unexpected character '%c'\n", *p); 504 | } else if (*p == '\0') { 505 | fprintf(stderr, "unexpected EOF\n"); 506 | } else { 507 | fprintf(stderr, "bad character \"\\x%02X\"\n", *p); 508 | } 509 | p = NULL; 510 | break; 511 | } 512 | 513 | return p; 514 | } 515 | 516 | static const char *parse_unary(const char *p, bitoffset_t *value) 517 | { 518 | if (accept(&p, '+')) { 519 | p = parse_unary(p, value); 520 | } else if (accept(&p, '-')) { 521 | if ((p = parse_unary(p, value))) 522 | *value = -(*value); 523 | } else { 524 | p = parse_factor(p, value); 525 | } 526 | return p; 527 | } 528 | 529 | static const char *parse_muldiv(const char *p, bitoffset_t *value) 530 | { 531 | if ((p = parse_unary(p, value))) { 532 | int op; 533 | bitoffset_t rhs; 534 | while ((op = accept_any(&p, "*/")) && (p = parse_unary(p, &rhs))) 535 | *value = (op == '*') ? (*value * rhs / 8) : 8 * *value / rhs; 536 | } 537 | return p; 538 | } 539 | 540 | static const char *parse_addsub(const char *p, bitoffset_t *value) 541 | { 542 | if ((p = parse_muldiv(p, value))) { 543 | int op; 544 | bitoffset_t rhs; 545 | while ((op = accept_any(&p, "+-")) && (p = parse_muldiv(p, &rhs))) 546 | *value = (op == '+') ? *value + rhs : *value - rhs; 547 | } 548 | return p; 549 | } 550 | 551 | static const char *parse_expression(const char *p, bitoffset_t *value) 552 | { 553 | *value = 0; 554 | return parse_addsub(p, value); 555 | } 556 | 557 | static const char *parse_slice_offset(const char *p, bitoffset_t *offset) 558 | { 559 | if ((p = parse_expression(p, offset))) { 560 | if (peek(&p) != '\0' && peek(&p) != ':') { 561 | fprintf(stderr, "junk '%s' after slice offset\n", p); 562 | p = NULL; 563 | } 564 | } 565 | return p; 566 | } 567 | 568 | static int parse_offset(const char *p, bitoffset_t *offset) 569 | { 570 | if ((p = parse_slice_offset(p, offset))) { 571 | if (peek(&p)) { 572 | fprintf(stderr, "junk '%s' after offset\n", p); 573 | p = NULL; 574 | } 575 | } 576 | return p != NULL; 577 | } 578 | 579 | static int parse_slice(const char *p, struct slice *slice) 580 | { 581 | slice->s = 1; 582 | slice->l = 0; 583 | 584 | /* L:r:s */ 585 | if (!peek(&p) || (*p != ':' && !(p = parse_slice_offset(p, &slice->l)))) 586 | return 0; 587 | slice->r = !peek(&p) ? slice->l+1 : (bitoffset_t)(~(bitsize_t)0 >> 1); 588 | accept(&p, ':'); 589 | 590 | /* l:R:s */ 591 | slice->relative = !!accept(&p, '+'); 592 | if (!peek(&p)) return 1; 593 | if (*p != ':' && !(p = parse_slice_offset(p, &slice->r))) 594 | return 0; 595 | accept(&p, ':'); 596 | 597 | /* l:r:S */ 598 | if (!peek(&p)) return 1; 599 | if (*p != ':' && !(p = parse_slice_offset(p, &slice->s))) 600 | return 0; 601 | 602 | if (peek(&p)) { 603 | fprintf(stderr, "junk '%s' after slice\n", p); 604 | return 0; 605 | } 606 | 607 | return 1; 608 | } 609 | 610 | static int handle_slice_option(const char *slice) 611 | { 612 | /* Brace expansions */ 613 | const char *p = strchr(slice, '{'); 614 | if (p) { 615 | size_t n; 616 | char *buf, *end; 617 | const char *start, *q = p + 1; 618 | do { 619 | n = strspn(q, "0123456789"); 620 | q += n; 621 | if (n && accept(&q, '-')) { 622 | n = strspn(q, "0123456789"); 623 | q += n; 624 | } 625 | } while (n && accept(&q, ',')); 626 | if (*q != '}' || !n) { 627 | fprintf(stderr, "invalid expansion in slice '%s'\n", slice); 628 | return 0; 629 | } 630 | buf = malloc(strlen(slice) + 1); 631 | if (!buf) { 632 | fprintf(stderr, "out-of-memory allocating string '%s'\n", slice); 633 | return 0; 634 | } 635 | start = p; 636 | do { 637 | bitsize_t i, j; 638 | i = j = (bitsize_t)strtoull(start+1, (char **)&end, 10); 639 | if (end && *end == '-') 640 | j = (bitsize_t)strtoull(end+1, (char **)&end, 10); 641 | if (i > j) { j ^= i; i ^= j; j ^= i; } 642 | while (i <= j) { 643 | sprintf(buf, "%.*s%ju%s", (int)(p - slice), slice, i, q+1); 644 | if (!handle_slice_option(buf)) { 645 | free(buf); 646 | return 0; 647 | } 648 | i++; 649 | } 650 | } while ((start = end) && *end == ','); 651 | free(buf); 652 | return 1; 653 | } 654 | 655 | /* Reserve space in input.slices[] */ 656 | if (!(input.nslices & (input.nslices + 1))) { 657 | struct slice *new; 658 | if (input.slices == NULL) { 659 | new = malloc(sizeof(struct slice)); 660 | } else { 661 | size_t capacity = 2*(input.nslices + 1); 662 | new = realloc(input.slices, capacity*sizeof(struct slice)); 663 | } 664 | if (!new) { 665 | fprintf(stderr, "out-of-memory allocating slice %zu\n", 666 | input.nslices + 1); 667 | return 0; 668 | } 669 | input.slices = new; 670 | } 671 | 672 | /* Parse slice string */ 673 | if (!parse_slice(slice, &input.slices[input.nslices])) { 674 | fprintf(stderr, "invalid slice '%s'\n", slice); 675 | return 0; 676 | } 677 | input.nslices++; 678 | return 1; 679 | } 680 | 681 | static FILE *handle_message_file(const char *filename, size_t *size) 682 | { 683 | fpos_t start; 684 | FILE *in, *temp; 685 | char buf[BUFSIZ]; 686 | 687 | /* Initialize CRC for empty message */ 688 | if (!bigint_init(&input.checksum, input.crc.width)) 689 | return NULL; 690 | bigint_load_zeros(&input.checksum); 691 | crc(&input.crc, NULL, (*size = 0), &input.checksum); 692 | 693 | /* Get input stream for calculating CRC */ 694 | if ((in = !strcmp(filename, "-") ? stdin : fopen(filename, "rb")) == NULL) { 695 | fprintf(stderr, "open '%s' for reading failed\n", filename); 696 | return NULL; 697 | } 698 | 699 | temp = NULL; 700 | if (input.has_target) { 701 | if (in == stdin || fgetpos(in, &start) != 0) { 702 | /* 703 | * Modifying input message but got a non-seekable file stream. 704 | * As a workaround, copy the input message to a temporary file. 705 | */ 706 | if (input.verbose >= 1) 707 | fputs("creating temp file to store input message\n", stderr); 708 | if (!(temp = tmpfile())) { 709 | fputs("error creating temp file for input message\n", stderr); 710 | goto fail; 711 | } 712 | } 713 | if (fgetpos(temp ? temp : in, &start) != 0) { 714 | fputs("fgetpos() error for ", stderr); 715 | if (temp) fputs("temp file of ", stderr); 716 | fprintf(stderr, "input file '%s'\n", filename); 717 | goto fail; 718 | } 719 | } 720 | 721 | while (!feof(in)) { 722 | size_t n = fread(buf, sizeof(char), BUFSIZ, in); 723 | if (ferror(in)) { 724 | fprintf(stderr, "error reading message from '%s'\n", filename); 725 | goto fail; 726 | } else if (temp) { 727 | size_t i = 0; 728 | while (i < n) { 729 | size_t m = fwrite(buf + i, sizeof(char), n - i, temp); 730 | if (!m || ferror(temp)) { 731 | fputs("error writing to temp file\n", stderr); 732 | goto fail; 733 | } 734 | i += m; 735 | } 736 | } 737 | crc_append(&input.crc, buf, n, &input.checksum); 738 | *size += n; 739 | } 740 | 741 | if (input.has_target) { 742 | /* Rewind */ 743 | if (temp) { 744 | fclose(in); 745 | in = temp; 746 | } 747 | if (fsetpos(in, &start) != 0) { 748 | fputs("fsetpos() error for ", stderr); 749 | if (temp) fputs("temporary file of ", stderr); 750 | fprintf(stderr, "input file '%s'\n", filename); 751 | goto fail; 752 | } 753 | } 754 | 755 | return in; 756 | 757 | fail: 758 | if (input.has_target && temp != NULL) 759 | fclose(temp); 760 | fclose(in); 761 | return NULL; 762 | } 763 | 764 | static void input_crc(bitsize_t pos, struct bigint *checksum) 765 | { 766 | bigint_mov(checksum, &input.checksum); 767 | if (pos < input.bitlen) { 768 | if (!input.crc.reflect_in) 769 | pos = (pos & ~7) | (7 - (pos & 7)); 770 | crc_sparse_1bit(input.sparse, pos, checksum); 771 | } 772 | } 773 | 774 | /* Input array A[0..n] and work array B[0..n] */ 775 | static void merge_sort_recurse(bitsize_t *A, size_t n, bitsize_t *B) 776 | { 777 | const size_t m = n / 2; 778 | if (m > 0) { 779 | size_t i, j, k; 780 | merge_sort_recurse(B, m, A); 781 | merge_sort_recurse(A+m, n-m, B+m); 782 | for (i = j = 0, k = m; i < k; i++) { 783 | if (k == n || (j < m && B[j] <= A[k])) { 784 | A[i] = B[j++]; 785 | } else { 786 | A[i] = A[k++]; 787 | } 788 | } 789 | } 790 | } 791 | 792 | static int merge_sort(bitsize_t *A, size_t n) 793 | { 794 | bitsize_t *B; 795 | if ((B = calloc(n, sizeof(bitsize_t)))) { 796 | memcpy(B, A, n * sizeof(bitsize_t)); 797 | merge_sort_recurse(A, n, B); 798 | free(B); 799 | } 800 | return !!B; 801 | } 802 | 803 | static int write_adjusted(FILE *in, bitsize_t flips[], size_t n, FILE *out) 804 | { 805 | size_t m, size; 806 | if (!merge_sort(flips, n)) { 807 | fputs("out of memory for merge sort work space\n", stderr); 808 | return 0; 809 | } 810 | 811 | m = size = 0; 812 | while (size < input.len) { 813 | size_t i, j; 814 | char buf[BUFSIZ]; 815 | if (size >= input.len - input.pad) { 816 | j = (input.len - size) < BUFSIZ ? (input.len - size) : BUFSIZ; 817 | memset(&buf, 0, j); 818 | } else if (!feof(in)) { 819 | j = fread(buf, sizeof(char), BUFSIZ, in); 820 | if (ferror(in)) { 821 | fputs("error reading input message\n", stderr); 822 | break; 823 | } 824 | } else { 825 | fprintf(stderr, "adjusted message has wrong length: %zu != %zu\n", 826 | size, input.len); 827 | break; 828 | } 829 | 830 | while (m < n && flips[m] / 8 < size + j) { 831 | buf[(flips[m] / 8) - size] ^= 1 << (flips[m] % 8); 832 | m++; 833 | } 834 | 835 | i = 0; 836 | while (i < j) { 837 | size_t ret = fwrite(buf + i, sizeof(char), j - i, out); 838 | if (!ret || ferror(out)) { 839 | fputs("error writing adjusted message\n", stderr); 840 | return 0; 841 | } 842 | i += ret; 843 | } 844 | size += i; 845 | } 846 | 847 | return size == input.len; 848 | } 849 | 850 | int main(int argc, char *argv[]) 851 | { 852 | int exit_code; 853 | bitoffset_t ret; 854 | 855 | /* Parse command-line interface arguments */ 856 | if ((exit_code = handle_args(argc, argv))) 857 | goto finish; 858 | 859 | /* Print CRC to stdout and exit if no target checksum given */ 860 | if (!input.has_target) { 861 | bigint_print(&input.checksum); 862 | puts(""); 863 | exit_code = 0; 864 | goto finish; 865 | } 866 | 867 | /* Forge */ 868 | ret = forge(&input.target, input_crc, input.bits, input.nbits); 869 | 870 | if (ret < 0) { 871 | fprintf(stderr, "FAIL! try giving %jd mutable bits more (got %zu)\n", 872 | -ret, input.nbits); 873 | exit_code = 6; 874 | goto finish; 875 | } 876 | 877 | /* Show flipped bits */ 878 | if (input.verbose >= 1) { 879 | bitoffset_t i; 880 | fprintf(stderr, "flip[%jd] = {", ret); 881 | for (i = 0; i < ret; i++) { 882 | const char *fmt = &", %ju.%ju"[!i]; 883 | fprintf(stderr, fmt, input.bits[i]/8, input.bits[i]%8); 884 | } 885 | fprintf(stderr, " }\n"); 886 | } 887 | 888 | if (!write_adjusted(input.in, input.bits, ret, input.out)) { 889 | exit_code = 7; 890 | goto finish; 891 | } 892 | 893 | /* Success! */ 894 | exit_code = 0; 895 | 896 | finish: 897 | if (input.in) fclose(input.in); 898 | if (input.out) fclose(input.out); 899 | crc_sparse_delete(input.sparse); 900 | bigint_destroy(&input.checksum); 901 | bigint_destroy(&input.target); 902 | bigint_destroy(&input.crc.poly); 903 | bigint_destroy(&input.crc.init); 904 | bigint_destroy(&input.crc.xor_out); 905 | free(input.slices); 906 | free(input.bits); 907 | return exit_code; 908 | } 909 | --------------------------------------------------------------------------------