/*
* CRC-32 forcer (Rust)
*
* Copyright (c) 2021 Project Nayuki
* https://www.nayuki.io/page/forcing-a-files-crc-to-any-value
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (see COPYING.txt).
* If not, see .
*/
use std::io;
use io::Read;
use io::Seek;
use io::Write;
use std::str::FromStr;
/*---- Main application ----*/
fn main() {
if let Some(msg) = submain(std::env::args().collect()) {
eprintln!("{}", msg);
std::process::exit(1);
}
}
fn submain(argv: Vec) -> Option {
// Handle arguments
if argv.len() != 4 {
return Some(format!("Usage: {} FileName ByteOffset NewCrc32Value", argv[0]));
}
let offset = match u64::from_str(&argv[2]) {
Ok(x) => x,
Err(_) => return Some("Error: Invalid byte offset".to_string()),
};
if argv[3].len() != 8 {
return Some("Error: Invalid new CRC-32 value".to_string());
}
let newcrc = match u32::from_str_radix(&argv[3], 16) {
Ok(x) => x.reverse_bits(),
Err(_) => return Some("Error: Invalid new CRC-32 value".to_string()),
};
let file = std::path::Path::new(&argv[1]);
if !file.is_file() {
return Some(format!("Error: File does not exist: {}", argv[1]));
}
// Process the file
match modify_file_crc32(&file, offset, newcrc, true) {
Ok(_) => None,
Err(e) => {
let prefix = if e.kind() == io::ErrorKind::InvalidInput
{ "Error" } else { "I/O error" };
Some(format!("{}: {}", prefix, e.to_string()))
}
}
}
/*---- Main function ----*/
// Public library function.
pub fn modify_file_crc32(file: &std::path::Path, offset: u64, newcrc: u32, printstatus: bool)
-> io::Result<()> {
let length = std::fs::metadata(file)?.len();
let mut raf = std::fs::OpenOptions::new().read(true).write(true).open(file)?;
if length < 4 || offset > length - 4 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput, "Byte offset plus 4 exceeds file length"));
}
// Read entire file and calculate original CRC-32 value
let crc: u32 = get_crc32(&mut raf)?;
if printstatus {
println!("Original CRC-32: {:08X}", crc.reverse_bits());
}
// Compute the change to make
let delta = multiply_mod(
reciprocal_mod(pow_mod(2, (length - offset) * 8)),
u64::from(crc ^ newcrc)) as u32;
// Patch 4 bytes in the file
raf.seek(io::SeekFrom::Start(offset))?;
let mut bytes4 = [0u8; 4];
raf.read_exact(&mut bytes4)?;
for (i, b) in bytes4.iter_mut().enumerate() {
*b ^= (delta.reverse_bits() >> (i * 8)) as u8;
}
raf.seek(io::SeekFrom::Start(offset))?;
raf.write_all(&bytes4)?;
if printstatus {
println!("Computed and wrote patch");
}
// Recheck entire file
assert_eq!(get_crc32(&mut raf)?, newcrc, "Failed to update CRC-32 to desired value");
if printstatus {
println!("New CRC-32 successfully verified");
}
Ok(())
}
/*---- Utilities ----*/
// Generator polynomial. Do not modify, because there are many dependencies
const POLYNOMIAL: u64 = 0x104C11DB7;
fn get_crc32(raf: &mut std::fs::File) -> io::Result {
raf.seek(io::SeekFrom::Start(0))?;
let mut crc: u32 = !0;
let mut buffer = [0u8; 32 * 1024];
loop {
let n: usize = raf.read(&mut buffer)?;
if n == 0 {
return Ok(!crc);
}
for b in &buffer[.. n] {
for i in 0 .. 8 {
crc ^= u32::from(*b >> i) << 31;
crc = (crc << 1) ^ ((crc >> 31) * (POLYNOMIAL as u32));
}
}
}
}
/*---- Polynomial arithmetic ----*/
// Returns polynomial x multiplied by polynomial y modulo the generator polynomial.
fn multiply_mod(mut x: u64, mut y: u64) -> u64 {
let degree: i32 = get_degree(POLYNOMIAL);
assert_eq!(x >> degree, 0);
assert_eq!(y >> degree, 0);
// Russian peasant multiplication algorithm
let mut z: u64 = 0;
while y != 0 {
z ^= x * (y & 1);
y >>= 1;
x <<= 1;
x ^= (x >> degree) * POLYNOMIAL;
}
assert_eq!(z >> degree, 0);
z
}
// Returns polynomial x to the power of natural number y modulo the generator polynomial.
fn pow_mod(mut x: u64, mut y: u64) -> u64 {
// Exponentiation by squaring
let mut z: u64 = 1;
while y != 0 {
if y & 1 != 0 {
z = multiply_mod(z, x);
}
x = multiply_mod(x, x);
y >>= 1;
}
z
}
// Computes polynomial x divided by polynomial y, returning the quotient and remainder.
fn divide_and_remainder(mut x: u64, y: u64) -> (u64,u64) {
assert!(y != 0, "Division by zero");
if x == 0 {
(0, 0)
} else {
let ydeg: i32 = get_degree(y);
let mut z: u64 = 0;
for i in (0 .. get_degree(x) - ydeg + 1).rev() {
let bit = x >> (i + ydeg) & 1;
x ^= bit * (y << i);
z |= bit << i;
}
(z, x)
}
}
// Returns the reciprocal of polynomial x with respect to the generator polynomial.
fn reciprocal_mod(mut x: u64) -> u64 {
assert_eq!(x >> get_degree(POLYNOMIAL), 0);
// Based on a simplification of the extended Euclidean algorithm
let mut y: u64 = x;
x = POLYNOMIAL;
let mut a: u64 = 0;
let mut b: u64 = 1;
while y != 0 {
let (q, r) = divide_and_remainder(x, y);
let c: u64 = a ^ multiply_mod(q, b);
x = y;
y = r;
a = b;
b = c;
}
assert_eq!(x, 1, "Reciprocal does not exist");
a
}
fn get_degree(x: u64) -> i32 {
63 - (x.leading_zeros() as i32)
}