Giter Site home page Giter Site logo

crchack's Introduction

crchack is a public domain tool to force CRC checksums of input messages to arbitrarily chosen values. The main advantage over existing CRC alteration tools is the ability to obtain the target checksum by changing non-contiguous input bits. Furthermore, crchack supports all CRC algorithms including custom CRC parameters. crchack is also an arbitrary-precision CRC calculator.

Usage

usage: ./crchack [options] file [target_checksum]

options:
  -o pos    byte.bit position of mutable input bits
  -O pos    position offset from the end of the input
  -b l:r:s  specify bits at positions l..r with step s
  -h        show this help
  -v        verbose mode

CRC parameters (default: CRC-32):
  -p poly   generator polynomial    -w size   register size in bits
  -i init   initial register value  -x xor    final register XOR mask
  -r        reverse input bytes     -R        reverse final register

Input message is read from file and the patched message is written to stdout. By default, crchack appends 4 bytes to the input producing a new message with the target CRC-32 checksum. Other CRC algorithms are defined using the CRC parameters. If target_checksum is not given, then crchack calculates the CRC checksum of the input message and writes the result to stdout.

Examples

[crchack]$ echo "hello" > foo
[crchack]$ ./crchack foo
363a3020
[crchack]$ ./crchack foo 42424242 > bar
[crchack]$ ./crchack bar
42424242
[crchack]$ xxd bar
00000000: 6865 6c6c 6f0a d2d1 eb7a                 hello....z

[crchack]$ echo "foobar" | ./crchack - DEADBEEF | ./crchack -
deadbeef

[crchack]$ echo "PING 1234" | ./crchack -
29092540
[crchack]$ echo "PING XXXX" | ./crchack -o5 - 29092540
PING 1234

In order to modify non-consecutive input message bits, specify the mutable bits with -b start:end:step switches using Python-style slices which represent the bits between positions start (inclusive) and end (exclusive) with successive bits step bits apart. If empty, start is the beginning of the message, end is the end of the message, and step equals 1 bit selecting all bits in between. For example, -b 4: selects all bits starting from the byte position 4 (note that -b 4 without the colon selects only the 32nd bit).

[crchack]$ echo "aXbXXcXd" | ./crchack -b1:2 -b3:5 -b6:7 - cafebabe | xxd
00000000: 61d6 6298 f763 4d64 0a                   a.b..cMd.
[crchack]$ echo -e "a\xD6b\x98\xF7c\x4Dd" | ./crchack -
cafebabe

[crchack]$ echo "1234PQPQ" | ./crchack -b 4: - 12345678
1234u>|7
[crchack]$ echo "1234PQPQ" | ./crchack -b :4 - 12345678
_MLPPQPQ
[crchack]$ echo "1234u>|7" | ./crchack - && echo "_MLPPQPQ" | ./crchack -
12345678
12345678

The byte position is optionally followed by a dot-separated bit position. For instance, -b0.32, -b2.16 and -b4.0 all select the same 32nd bit. Within a byte, bits are numbered from the least significant bit (0) to the most significant bit (7). Negative positions are treated as offsets from the end of the input. Built-in expression parser supports 0x-prefixed hexadecimal numbers as well as basic arithmetic operations +-*/. Finally, end can be defined relative to start by starting the position expression with unary + operator.

[crchack]$ python -c 'print("A"*32)' | ./crchack -b "0.5:+.8*32:.8" - 1337c0de
AAAaAaaaaaAAAaAaAaAaAaaAaAaaAAaA
[crchack]$ echo "AAAaAaaaaaAAAaAaAaAaAaaAaAaaAAaA" | ./crchack -
1337c0de

[crchack]$ echo "1234567654321" | ./crchack -b .0:-1:1 -b .1:-1:1 -b .2:-1:1 - baadf00d
0713715377223
[crchack]$ echo "0713715377223" | ./crchack -
baadf00d

In the latter example, repetition can be avoided by utilizing brace expansions:

[crchack]$ echo "1234567654321" | ./crchack -b ".{0-2}:-1:1" - baadf00d
0713715377223

Obtaining the target checksum is impossible if given an insufficient number of mutable bits. In general, the user should provide at least w bits where w is the width of the CRC register, e.g., 32 bits for CRC-32.

CRC algorithms

crchack works with all CRCs that use sane parameters, including all commonly used standardized CRCs. crchack defaults to CRC-32 and other CRC functions can be specified by passing the CRC parameters via command-line arguments.

[crchack]$ printf "123456789" > msg
[crchack]$ ./crchack -w8 -p7 msg                                     # CRC-8
f4
[crchack]$ ./crchack -w16 -p8005 -rR msg                             # CRC-16
bb3d
[crchack]$ ./crchack -w16 -p8005 -iffff -rR msg                      # MODBUS
4b37
[crchack]$ ./crchack -w32 -p04c11db7 -iffffffff -xffffffff -rR msg   # CRC-32
cbf43926

CRC RevEng (by Greg Cook) includes a comprehensive catalogue of cyclic redundancy check algorithms and their parameters. check.sh illustrates how to convert the CRC catalogue entries to crchack CLI flags.

How it works?

CRC is often described as a linear function in the literature. However, CRC implementations used in practice often differ from the theoretical definition and satisfy only a weak linearity property (^ denotes XOR operator):

CRC(x ^ y ^ z) = CRC(x) ^ CRC(y) ^ CRC(z), for |x| = |y| = |z|

crchack applies this rule to construct a system of linear equations such that the solution is a set of input bits which inverted yield the target checksum.

The intuition is that each input bit flip causes a fixed difference in the resulting checksum (independent of the values of the neighbouring bits). This, in addition to knowing the required difference, produces a system of linear equations over GF(2). crchack expresses the system in a matrix form and solves it with the Gauss-Jordan elimination algorithm.

Notice that the CRC function is treated as a "black box", i.e., the internal CRC parameters are used only for evaluation. Therefore, the same approach is applicable to any function that satisfies the weak linearity property.

Use cases

So why would someone want to forge CRC checksums? Because CRC32("It'S coOL To wRitE meSSAGes LikE this :DddD") == 0xDEADBEEF.

In a more serious sense, there exist a bunch of firmwares, protocols, file formats and standards that utilize CRCs. Hacking these may involve, e.g., modification of data so that the original CRC checksum remains intact. One interesting possibility is the ability to store arbitrary data (e.g., binary code) to checksum fields in packet and file headers.

crchack's People

Contributors

deffi420 avatar resilar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

crchack's Issues

Improve speed

The CRC computation is too slow for files >100MB. This can be improved a lot.

sonic

User-friendlier way to specify mutable bits for alphanumerical output

Proposed behavior:

  • -b 03FFFFC0@1024 selects 20 bits at position 1024
  • -b 80@30:40 selects 10 MSBs between positions 30 and 40
  • -b 2@0:-1:1 selects all bits except the first (apply at every bit position)

After implementing this, bit numbering could be changed to MSB -> LSB which is more intuitive IMO.

`forge()` does not write the modified message to `out`

The docs state that out is populated with the modified message but it is not:

* Output buffer `out` receives a modified message with the desired checksum.

The out argument is currently unused. I might be being overly pedantic since the docs are admitted to be incorrect, but still.

Thank you for this tool, by the way, it is quite helpful!

Clarification: prepending instead of appending, using -O option with negative values?

I'm trying to calculate a 4 byte value to PREPEND to a 8192 byte data file so as to get a specified CRC32 value 0x224E16C3 . My reading of the manual made me think I needed to use the option -O -8192 or maybe even -O -8196. But that does not work. Can you please clarify how to get a value to prepend? Test case below.

## create test file with hex AB repeated the entire file
perl -e '$x="AB" x 8192; print pack("H*", $x);' > fileAB.8192
hexdump fileAB.8192

## test regular append-mode
crchack fileAB.8192 224E16C3 > test-append
crchack test-append
# observe that the padding value is appended
hexdump test-append

##trying to create a value to PREPEND
crchack  -O -8192  fileAB.8192 224E16C3 > test-prepend
crchack test-prepend
# observe that the padding value is appended but I thought it would prepended?
hexdump test-prepend

crchack  -O -8196 fileAB.8192 224E16C3 > test-prepend
leads to error message: bits[0]=65568 exceeds message length (65567 bits)

Streaming version of crchack

crchack currently reads the whole input message into a memory buffer before starting the CRC calculation/forging process. Ideally, we would like to read the input message in chunks and perform the CRC calculation or CRC forging operation in one pass over the input. This, unfortunately, would require major changes to the code at the time being.

Deprecate -o/-O offset switches in favor of -b switches

Quoting myself from comment thread of issue #9 "Lazy -b evaluation"

I think we should consider deprecating offset switches -o/-O at some point because -b switches are now expressive and user-friendly enough to offer the same functionality: -o 42 and -b 42: are essentially identical. Similarly, -O 42 and -b -42: are equivalent to each other. The only disadvantage of -b command-line options is that appending to the input is unsupported when forging CRC checksums, whereas -o and -O do support appending to the end of the input message. However, the current append mechanism should probably be rewritten anyway to support prepending also (as discussed in issue #11).

Doesn't work with my specific case

I tried, without success, to use custom parameters to force CRC on a file that is verified with something that looks like the crc32-alder algorithm.

Here is the crc calculation code (that use a table):

uint32_t crc32_init(void)
{
return 0xffffffff;
}

uint32_t crc32_byte(uint32_t accum, uint8_t delta)
{
return table[(accum ^ delta) & 0xff] ^ (accum >> 8);
}

uint32_t get_crc(unsigned char *buffer, size_t buffer_size) {
uint32_t crc = crc32_init();
for (int i = 0; i < buffer_size; i++) {
crc = crc32_byte(crc, buffer[i]);
}
return crc;
}

Here is the code that has been used to generate the table (we can read that the polynomial is 0xedb88320):

Here are the parameters I tried and the message generated:

-w32 -pedb88320
FAIL! try giving 32 mutable bits more (got 32)

-w32 -pedb88320 -iffffffff
FAIL! try giving 32 mutable bits more (got 32)

-w32 -pedb88320 -iffffffff -xff -rR
FAIL! try giving 5 mutable bits more (got 32)

-w32 -pedb88320 -iffffffff -rR
FAIL! try giving 4 mutable bits more (got 32)

-w32 -pedb88320 -iffffffff -xff
FAIL! try giving 28 mutable bits more (got 32)

-w32 -pedb88320 -iffffffff -xff -r
FAIL! try giving 28 mutable bits more (got 32)

-w32 -pedb88320 -iffffffff -xff -R
FAIL! try giving 5 mutable bits more (got 32)

-w32 -pedb88320 -iffffffff -R
FAIL! try giving 4 mutable bits more (got 32)

-w32 -pedb88320 -iffffffff -r
FAIL! try giving 32 mutable bits more (got 32)

What am I doing wrong ?

Quines

Implement a feature to calculate CRC quines. For example:

$ printf "\xcc\x4f\xbb\x6a" | ./crchack -
cc4fbb6a
$ printf "\xff\xff\xff\xff" | ./crchack -
ffffffff
$ printf 'foo\x81\xc8\x73\xa7bar' | ./crchack -
81c873a7
$ printf 'foo\xe0\x2d\x7a\x0bbar' | ./crchack -
e02d7a0b

sagemath proof of concept:

def binvec(x, w=32): return vector(GF(2), x.digits(2, padto=w))

def crc32(msg):
    crc = 0xffffffff
    for c in msg.encode() if isinstance(msg, str) else msg:
        crc ^^= c & 0xff
        for _ in range(8):
            crc = (crc >> 1) ^^ 0xedb88320 if crc & 1 else crc >> 1
    return crc ^^ 0xffffffff

def h(x): return crc32(int(x).to_bytes(4, 'big'))
h0 = h(0) # crc32(b'\x00\x00\x00\x00')
diff = [h0 ^^ h(2^x) for x in range(32)]
A = matrix(GF(2), 32, [x.digits(2, padto=32) for x in diff]).transpose()

# Find a quine
sol = (A - identity_matrix(GF(2), 32)).solve_right(binvec(h0, 32))
quine = Integer(sol.list(), base=2)
print(hex(quine), quine == h(quine))
# 0xcc4fbb6a True
# Calculate all quines
def quines(h, w=32):
    def gf2vec(x): return vector(GF(2), x.digits(2, padto=w))
    h0 = h(0)
    diff = [h0 ^^ h(2^x) for x in range(w)]
    A = matrix(GF(2), w, [x.digits(2, padto=w) for x in diff]).transpose()
    Id = identity_matrix(GF(2), w)
    try: sol = (A - Id).solve_right(gf2vec(h0))
    except: return # No solutions
    for kernel in (A - Id).right_kernel():
        yield Integer((sol + kernel).list(), base=2)

def h(x, prefix=b'', suffix=b''):
    return crc32(prefix + int(x).to_bytes(4, 'big') + suffix)
for quine in quines(h, 32): print(hex(quine), quine == h(quine))
# 0xcc4fbb6a True
# 0xffffffff True

H = lambda x: h(x, b'foo', b'bar')
for quine in quines(H, 32):
    print(hex(quine), quine == H(quine))
# 0x81c873a7 True
# 0xe02d7a0b True

Lazy `-b` evaluation

Currently -b 4: for a long input (e.g., 1GB) is handled non-lazily by allocating a huge array to store all the input bits following the 4th byte. This is stupid. Be smarter.

Handle underdetermined matrices

Currently crchack works with determined and overdetermined systems of equations. In some cases an underdetermined system may have solutions which can be used to get the desired checksum value.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.