#memory #flip #modification #detect #bit

process_consistency

Hash your executable memory to ensure it stays the same

1 unstable release

0.5.0 Sep 5, 2022

#5 in #flip

MIT/Apache

28KB
444 lines

process_consistency

A small background checker to ensure your executable code doesn't change, e.g. due to cosmic rays, rowhammer attacks, etc. To this end it periodically computes a checksum of all your executable pages in memory.

Compatible with Windows and Linux only

Crates.io Documentation Lines of code Crates.io

Basic Usage

  use process_consistency::ProcessConsistencyChecker;
  std::thread::spawn(|| {ProcessConsistencyChecker::new().run(|error| {panic!("Memory Error: {:#?}", &error)}).unwrap()});

The call to run() only returns when it encounters (non-memory) errors. If a diverging hash is found, the provided callback is called with additional info, including which library/binary was affected.

SAFETY

This crate reads pointers from addresses provided by the operating system. This is only safe if these memory regions stay mapped into the process. Gernerally this is not a problem, but if you unload a shared library (e.g. by calling FreeLibrary on Windows, or dlclose on Linux) this causes race conditions that can lead to this library reading unmapped memory

Running with skip_libs(true) should be safe even in the presence of FreeLibrary/dlclose calls

Advanced Usage

You can decrease the search radius, e.g. if you are not concerned about shared libraries (including those of your OS) you can use

  use process_consistency::ProcessConsistencyChecker;
  std::thread::spawn(|| {ProcessConsistencyChecker::new().skip_libs(true).search_once(true).run(|error| {panic!("Memory Error: {:#?}", &error)}).unwrap()});

On the other hand if you are paranoid, you might find situations where also considering pages marked as executable but writable is desirable:

  use process_consistency::ProcessConsistencyChecker;
  std::thread::spawn(|| {ProcessConsistencyChecker::new().include_writable_code(true).run(|error| {panic!("Memory Error: {:#?}", &error)}).unwrap()});

You can also change how often the checks should be run:

  use std::time::Duration;
  use process_consistency::ProcessConsistencyChecker;
  std::thread::spawn(|| {ProcessConsistencyChecker::new().check_period(Duration::from_secs(60)).run(|error| {panic!("Memory Error: {:#?}", &error)}).unwrap()});

To get a rough idea of the implications of the chosen parameters, or just to figure out which shared libraries are loaded (hint: more than you think), there is a benchmark call

  use std::time::Duration;
  use process_consistency::ProcessConsistencyChecker;
  println!("{:#?}", ProcessConsistencyChecker::new().benchmark().unwrap());

Hash Algorithm

The used hash algorithm is determined by feature flags. Blake3 is the default.

To use with blake3 hash use

[dependencies]
process_consistency = "0.1.0"

To use with crc64 hash use

[dependencies]
process_consistency = { version = "0.1.0", default-features = false, features = ["crc64"] }

Blake3 is a cryptographically strong hash, but if you are just worried about cosmic rays you get about a 2x speedup with crc64 (in release mode!, in debug mode blake3 is faster). Crc64 also has slightly fewer dependencies

License

process_consistency is dual-licensed under

Dependencies

~131MB
~2M SLoC