initial commit
This commit is contained in:
commit
f3a453a88e
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -0,0 +1,16 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "fakerand"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "fakerand"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
|
@ -0,0 +1,23 @@
|
|||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,11 @@
|
|||
CARGO_BUILD=cargo build --release
|
||||
TARGET_DIR=target/release
|
||||
LIB_NAME=libfakerand.so
|
||||
INSTALL_DIR=/usr/lib
|
||||
|
||||
all: build
|
||||
|
||||
build:
|
||||
$(CARGO_BUILD)
|
||||
|
||||
.PHONY: all build
|
|
@ -0,0 +1,39 @@
|
|||
# libfakerand
|
||||
|
||||
## Introduction
|
||||
|
||||
`libfakerand` intercepts various system calls that programs use to
|
||||
retrieve entropy. It replaces the entropy which would otherwise be
|
||||
used with the one supplied by the user. This means you can force
|
||||
programs to use a fixed value instead of a random one.
|
||||
|
||||
`libfakerand` overrides the following functions:
|
||||
* `rand()`
|
||||
* `read()` function for `/dev/urandom` and `/dev/random`
|
||||
* `getrandom()`
|
||||
* `RAND_bytes()`
|
||||
|
||||
## Usage
|
||||
|
||||
You may use env variables `LD_PRELOAD` and `FAKERAND` along with a command:
|
||||
```
|
||||
LD_PRELOAD=./target/release/libfakerand.so FAKERAND=0 openssl rand -hex 16
|
||||
```
|
||||
|
||||
Alternatively, set environment variables so that all programs running in that shell are effected:
|
||||
```
|
||||
export LD_PRELOAD=/usr/lib/fakerand/fakerand.so
|
||||
export FAKERAND=42
|
||||
```
|
||||
|
||||
If you will be using it this way, it may make sense to place the .so file in `/usr/lib/fakerand`
|
||||
|
||||
## Notes
|
||||
Statically linked libraries can't be overriden by `LD_PRELOAD`. For
|
||||
this reason, `libfakerand` can't override cases such as:
|
||||
|
||||
```
|
||||
FAKERAND=42 LD_PRELOAD=./target/release/libfakerand.so awk 'BEGIN{srand(); print rand()}' 2>/dev/null
|
||||
```
|
||||
|
||||
This is because `awk` is statically linked.
|
|
@ -0,0 +1,178 @@
|
|||
use std::env;
|
||||
use std::mem;
|
||||
use std::ffi::CStr;
|
||||
use libc::{
|
||||
c_int,
|
||||
c_uchar,
|
||||
c_uint,
|
||||
c_void,
|
||||
dlsym,
|
||||
getrandom as libc_getrandom,
|
||||
RTLD_NEXT,
|
||||
ssize_t
|
||||
};
|
||||
|
||||
fn fakerand_value() -> Option<i32> {
|
||||
env::var("FAKERAND").ok().and_then(|val| val.parse().ok())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rand() -> c_int {
|
||||
println!("rand() called");
|
||||
if let Some(fakerand) = fakerand_value() {
|
||||
println!("rand() value {}", fakerand);
|
||||
return fakerand;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let symbol = dlsym(RTLD_NEXT, "rand\0".as_ptr() as *const _);
|
||||
|
||||
if !symbol.is_null() {
|
||||
let original_rand: extern "C" fn() -> c_int = std::mem::transmute(symbol);
|
||||
return original_rand();
|
||||
} else {
|
||||
eprintln!("Error: original rand() not found");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn read(fd: c_int, buf: *mut c_void, count: usize) -> ssize_t {
|
||||
static mut ORIGINAL_READ: Option<extern "C" fn(c_int, *mut c_void, usize) -> ssize_t> = None;
|
||||
|
||||
unsafe {
|
||||
if ORIGINAL_READ.is_none() {
|
||||
let symbol = dlsym(RTLD_NEXT, "read\0".as_ptr() as *const _);
|
||||
if symbol.is_null() {
|
||||
eprintln!("Error: original read() not found");
|
||||
std::process::exit(1);
|
||||
}
|
||||
ORIGINAL_READ = Some(std::mem::transmute(symbol));
|
||||
}
|
||||
|
||||
let original_read = ORIGINAL_READ.unwrap();
|
||||
|
||||
let path = format!("/proc/self/fd/{}", fd);
|
||||
let mut actualpath = [0u8; 1024];
|
||||
|
||||
let len = libc::readlink(
|
||||
path.as_ptr() as *const i8,
|
||||
actualpath.as_mut_ptr() as *mut i8,
|
||||
actualpath.len() as _
|
||||
);
|
||||
|
||||
if len != -1 {
|
||||
let actualpath = CStr::from_ptr(actualpath.as_ptr() as *const i8).to_str().unwrap();
|
||||
if (actualpath == "/dev/urandom" || actualpath == "/dev/random") && fakerand_value().is_some() {
|
||||
let fakerand = fakerand_value().unwrap();
|
||||
*(buf as *mut c_int) = fakerand;
|
||||
return std::mem::size_of::<c_int>() as ssize_t;
|
||||
}
|
||||
}
|
||||
|
||||
original_read(fd, buf, count)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn getrandom(buf: *mut c_void, buflen: usize, flags: c_uint) -> ssize_t {
|
||||
if let Some(fakerand) = fakerand_value() {
|
||||
unsafe {
|
||||
*(buf as *mut c_int) = fakerand;
|
||||
}
|
||||
return std::mem::size_of::<c_int>() as ssize_t;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
libc_getrandom(buf, buflen, flags)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn RAND_bytes(buf: *mut c_uchar, num: c_int) -> c_int {
|
||||
if let Some(fakerand) = fakerand_value() {
|
||||
unsafe {
|
||||
for i in 0..num {
|
||||
*buf.add(i as usize) = fakerand as c_uchar;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let symbol = dlsym(RTLD_NEXT, "RAND_bytes\0".as_ptr() as *const _);
|
||||
if !symbol.is_null() {
|
||||
let original_rand_bytes: extern "C" fn(*mut c_uchar, c_int) -> c_int = mem::transmute(symbol);
|
||||
return original_rand_bytes(buf, num);
|
||||
} else {
|
||||
eprintln!("Error: original RAND_bytes() not found");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs::OpenOptions;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_rand_with_fakerand() {
|
||||
env::set_var("FAKERAND", "42");
|
||||
assert_eq!(rand(), 42);
|
||||
env::remove_var("FAKERAND");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rand_without_fakerand() {
|
||||
env::remove_var("FAKERAND");
|
||||
let rand_value = rand();
|
||||
// TODO: Does this mean the test will fail once in a while?
|
||||
assert!(rand_value != 42);
|
||||
assert!(rand_value >= 0 && rand_value <= libc::RAND_MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_with_fakerand() {
|
||||
env::set_var("FAKERAND", "42");
|
||||
|
||||
let file = OpenOptions::new().read(true).open("/dev/urandom").expect("Failed to open /dev/urandom");
|
||||
let fd = file.as_raw_fd();
|
||||
let mut buf = [0u8; 4];
|
||||
let result = read(fd, buf.as_mut_ptr() as *mut c_void, buf.len());
|
||||
|
||||
assert_eq!(result, std::mem::size_of::<c_int>() as ssize_t);
|
||||
assert_eq!(buf, [42, 0, 0, 0]);
|
||||
|
||||
env::remove_var("FAKERAND");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_without_fakerand() {
|
||||
env::remove_var("FAKERAND");
|
||||
|
||||
let file = OpenOptions::new().read(true).open("/dev/urandom").expect("Failed to open /dev/urandom");
|
||||
let fd = file.as_raw_fd();
|
||||
let mut buf = [0u8; 4];
|
||||
let result = read(fd, buf.as_mut_ptr() as *mut c_void, buf.len());
|
||||
|
||||
assert_eq!(result as usize, buf.len());
|
||||
assert!(buf != [0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rand_bytes_with_fakerand() {
|
||||
env::set_var("FAKERAND", "42");
|
||||
|
||||
let mut buf = [0u8; 4];
|
||||
let result = RAND_bytes(buf.as_mut_ptr(), buf.len() as c_int);
|
||||
|
||||
assert_eq!(result, 1);
|
||||
assert_eq!(buf, [42, 42, 42, 42]);
|
||||
|
||||
env::remove_var("FAKERAND");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue